Advanced Java Streams
1. Introduction
Java Streams provide a high-level abstraction for processing sequences of elements, allowing developers to write clean and efficient code for bulk operations on collections. This lesson covers advanced features of Java Streams, including operations, collectors, and parallel processing.
2. Key Concepts
What is a Stream?
A Stream is a sequence of elements that can be processed in parallel or sequentially. Streams do not store data; they operate on data from a source, such as a collection, an array, or I/O channels.
Stream vs. Collection
- Streams are not data structures; they do not hold elements.
- Streams support functional-style operations and allow for lazy evaluation.
- Collections are data structures that store elements.
3. Stream Operations
Stream operations can be categorized as intermediate and terminal operations.
Intermediate Operations
These operations return a new Stream and are lazy. Examples include filter
, map
, and sorted
.
List names = Arrays.asList("John", "Jane", "Jack");
List filteredNames = names.stream()
.filter(name -> name.startsWith("J"))
.collect(Collectors.toList());
Terminal Operations
These operations produce a result or a side-effect and close the stream. Examples include forEach
, collect
, and reduce
.
List names = Arrays.asList("John", "Jane", "Jack");
names.stream()
.forEach(System.out::println);
4. Collectors
Collectors are used to accumulate elements into collections or other data structures. The most common collector is Collectors.toList()
.
Using Collectors
List names = Arrays.asList("John", "Jane", "Jack");
List collectedNames = names.stream()
.collect(Collectors.toList());
Grouping and Partitioning
Collectors also allow grouping and partitioning of data:
Map> groupedByLength = names.stream()
.collect(Collectors.groupingBy(String::length));
Map> partitionedNames = names.stream()
.collect(Collectors.partitioningBy(name -> name.startsWith("J")));
5. Parallel Streams
Parallel Streams allow for parallel processing of data, taking advantage of multi-core architectures.
Creating Parallel Streams
List names = Arrays.asList("John", "Jane", "Jack");
names.parallelStream()
.forEach(System.out::println);
When to Use Parallel Streams
Use Parallel Streams for large datasets or computationally intensive tasks. However, avoid using them for small datasets due to the overhead of thread management.
6. Best Practices
- Prefer using Stream API for bulk operations over manual iteration.
- Use parallel streams judiciously; measure performance.
- Utilize method references for cleaner code.
- Be mindful of stateful operations within streams as they may cause performance issues.
7. FAQ
What is the difference between map()
and flatMap()
?
map()
transforms each element, while flatMap()
flattens the result of mapping to a single stream.
Can I modify my data while using Streams?
No, Streams are designed to be functional and should not modify the source data.
What is lazy evaluation in Streams?
Lazy evaluation means that computations on the stream are only performed when necessary, optimizing performance.