Understanding the Differences Between forEachOrdered and forEach in Java Streams
Java 8 introduced the Stream API, which provides powerful methods for processing collections. Among these, forEachOrdered and forEach serve similar purposes but behave differently when it comes to element ordering. Understanding these differences is crucial for writing correct and performant code.
The forEachOrdered Method
The forEachOrdered method enforces a deterministic processing order regardless of whether the stream is sequential or parallel. It processes elements exactly as they appear in the source collection.
Core characteristics:
- Order preservation: Elements are always processed in their original sequence from the source collection
- Consistency: The output order remains stable across multiple executions
- Use case: Ideal for operations requiring predictable iteration order
Example:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.parallelStream().forEachOrdered(System.out::print);
// Always outputs: 12345
In this example, eventhough the stream is parallel, forEachOrdered ensures the numbers are printed in ascending order.
The forEach Method
The forEach method iterates over each element and applies the provided action. However, it does not guarantee any particular order of execution, especially when used with parallel streams.
Core characteristics:
- No ordering guarantee: Element processing order is undefined in parallel streams
- Performance benefit: Can leverage parallel processing without synchronization overhead
- Use case: Suitable when processing order is irrelevant to the result
Example:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.parallelStream().forEach(System.out::print);
// Possible outputs: 31524, 43215, 52341, etc.
Here, the output sequence varies between runs because parallel processing dispatches elements to multiple threads without preserving order.
Decision Guide
| Scenario | Recommended Method |
|---|---|
| Need deterministic output | forEachOrdered |
| Order is irrelevant | forEach |
| Large datasets, parallel processing | forEach (if order-independent) |
| Aggregating results that depend on order | forEachOrdered |
When processing a list of transactions where each requires logging with timestamps, use forEachOrdered to maintain the chronological sequence. When aggregating data into a Set or computing a sum, forEach suffices since the final result remains the same regardless of processing order.
The performance impact is significant: forEachOrdered in parallel streams incurs additional coordination overhead to maintain order, while forEach can process elements freely across threads. For a collection of 100,000 elements, forEachOrdered may perform noticeab slower than forEach in parallel scenarios.
// Order-independent computation - use forEach
Set<String> uniqueValues = new HashSet<>();
collection.parallelStream().forEach(item -> uniqueValues.add(item.getValue()));
// Order-dependent operation - use forEachOrdered
StringBuilder builder = new StringBuilder();
collection.parallelStream().forEachOrdered(item -> builder.append(item.getValue()));