Parallel Streams
One pipeline can execute in parallel
A regular stream() executes sequentially.
A collection's parallelStream() or a call to parallel()
switches the pipeline to parallel mode: element processing may be split into parts
and performed at the same time.
long count = List.of(10, 20, 30, 40, 50).parallelStream()
.filter(value -> value >= 30)
.count();
The meaning of the result is the same as with a sequential stream. The difference is in how the work is executed, not in the operation API.
Parallel does not automatically mean faster
Splitting work, coordinating parts, and combining the result all take time themselves. On a small collection, that cost can easily be greater than the benefit of parallelism. The gain depends on the amount of data, the cost of the operation per element, how well the source can be split, and the available computing resources.
Side effects become more dangerous
A lambda inside a parallel pipeline may execute at the same time for different elements. If several calls modify the same shared mutable state, races and unpredictable results can appear.
List<Integer> result = IntStream.range(0, 1_000)
.parallel()
.filter(value -> value % 2 == 0)
.boxed()
.toList();
Here, each step describes a data transformation and does not modify one shared external list.
This style is much safer than adding elements to a single mutable collection from forEach.
Execution order and result order are different things
Even when a stream has an encounter order, individual elements may be processed at different times.
forEach on a parallel stream is not required to perform the action in source order.
If the side-effect order truly matters, forEachOrdered exists,
but preserving order can reduce the benefit of parallelism.
The combining operation must be correct for parallel execution
With reduce, parts of a stream can be reduced separately
and then combined. That is why the combining operation must be associative:
grouping the computations differently must not change the result.
Addition works; subtraction does not.
int sum = IntStream.rangeClosed(1, 100)
.parallel()
.reduce(0, Integer::sum);
A parallel stream is most useful for sufficiently large computational work that can be performed independently over parts of the data. For blocking operations, small collections, and code with shared mutable state, a sequential version often remains clearer and more predictable.