The Java Streams API, introduced in Java 8, brought functional programming capabilities to Java, enabling developers to process collections of data in a declarative and concise manner. It allows operations like filtering, mapping, and reducing to be performed efficiently and fluently.
1. What Is the Streams API?
The Streams API provides a pipeline for transforming and processing data from a source (like a collection or array) through a series of intermediate operations, ending with a terminal operation.
2. Stream Pipeline Structure
java
List<String> names = List.of("Alice", "Bob", "Charlie");
names.stream() // Source
.filter(name -> name.length() > 3) // Intermediate operation
.map(String::toUpperCase) // Intermediate operation
.forEach(System.out::println); // Terminal operation
3. Key Components of Streams
- Source: Collection, array, generator, or I/O channel
- Intermediate Operations:
filter()
, map()
, sorted()
— lazy operations - Terminal Operations:
collect()
, forEach()
, reduce()
— triggers execution - Pipeline: Chain of operations that are fused and lazily evaluated
4. Common Stream Operations
OperationDescriptionExamplefilter()
Filters elementsfilter(s -> s.startsWith("A"))map()
Transforms elementsmap(String::toUpperCase)sorted()
Sorts elementssorted()collect()
Accumulates elements into a resultcollect(Collectors.toList())reduce()
Reduces elements to a single valuereduce(0, Integer::sum)distinct()
Removes duplicatesdistinct()limit()
Limits outputlimit(5)
5. Stream vs Collection
FeatureStreamCollectionStores data?NoYesConsumed once?YesNoLazy evaluation?YesNoSupports parallelism?YesNo (manually)
6. Example: Filtering and Collecting
java
List<String> names = List.of("Alice", "Bob", "Amanda", "Brian");
List<String> result = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
System.out.println(result); // [Alice, Amanda]
7. Parallel Streams
Streams can be executed in parallel with .parallelStream()
:
java
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
int sum = numbers.parallelStream().reduce(0, Integer::sum);
Parallel streams improve performance on large datasets with multi-core CPUs, but they require careful management.
8. Avoiding Common Pitfalls
- Don’t reuse streams: They're consumable.
- Avoid stateful operations: Keep operations stateless for thread safety.
- Watch for side effects: Use pure functions in lambdas.
9. Stream API + Optional + Collectors
You can combine streams with Java’s Optional
and Collectors
for elegant data processing:
java
Optional<String> longest = names.stream()
.max(Comparator.comparingInt(String::length));
10. Conclusion
The Java Streams API is a game-changer for processing collections in a functional style. It leads to cleaner, more readable, and efficient code. Mastering streams allows you to write powerful data pipelines with minimal effort.