Why parallel stream get collected sequentially in Java 8
Why forEach
prints numbers in random order, while collect
always collects elements in original order, even from parallel stream?
Integer[] intArray = {1, 2, 3, 4, 5, 6, 7, 8};
List<Integer> listOfIntegers = new ArrayList<>(Arrays.asList(intArray));
System.out.println("Parallel Stream: ");
listOfIntegers
.stream()
.parallel()
.forEach(e -> System.out.print(e + " "));
System.out.println();
// Collectors
List<Integer> l = listOfIntegers
.stream()
.parallel()
.collect(Collectors.toList());
System.out.println(l);
Output:
Parallel Stream:
8 1 6 2 7 4 5 3
[1, 2, 3, 4, 5, 6, 7, 8]
There are two different kinds of "ordering" going on here, which makes the discussion confusing.
One kind is encounter order, which is defined in the streams documentation. A good way to think about this is the spatial or left-to-right order of elements in the source collection. If the source is a List
, consider the earlier elements being to the left of later elements.
There is also processing or temporal order, which isn't defined in the documentation, but which is the time order in which elements are processed by different threads. If the elements of a list are being processed in parallel by different threads, a thread might process the rightmost element in the list before the leftmost element. But the next time it might not.
Even when computations are done in parallel, most Collectors
and some terminal operations are carefully arranged so that they preserve encounter order from the source through to the destination, independently of the temporal order in which different threads might process each element.
Note that the forEach
terminal operation does not preserve encounter order. Instead, it's run by whatever thread happens to produce the next result. If you want something like forEach
that preserves encounter order, use forEachOrdered
instead.
See also the Lambda FAQ for further discussion about ordering issues.
The Collectors.toList
method specifies that the returned Collector
adds elements to the list in encounter order.
Returns:
a Collector which collects all the input elements into a List, in encounter order
It doesn't matter whether the Stream
is parallel; the order is preserved.
Additionally, looking at the Collectors
source code, the returned Collector
calls addAll
on an ArrayList
when merging, and that preserves the order. E.g. if one thread has {1, 2} and the next thread has {3, 4}, then the call to addAll
yields {1, 2, 3, 4}. Also, the returned Collector
doesn't have the UNORDERED
characteristic.