Streams can be sequential or parallel. Operations on a sequential stream are processed in serial using one thread. Operations on a parallel stream are processed in parallel using multiple threads. You do not need to take additional steps to process streams because they are sequential or parallel. All you need to do is call the appropriate method that produces sequential or parallel stream. Everything else is taken care of by the Streams API
Most of the methods in the Streams API produce sequential streams by default. To produce a parallel stream
from a collection such as a
List or a
Set, you need to call the
parallelStream() method of the
parallel() method on a stream to convert a sequential stream into a parallel stream. Conversely, use the
sequential() method on a stream to convert a parallel stream into a sequential stream.
The following snippet of code shows serial processing of the stream pipeline because the stream is sequential:
List<Integer> listOfIntegers = List.of(1, 2, 3, 4, 5, 6, 7, 8); System.out.println("Sequential Stream: "); listOfIntegers .stream() .forEach(e -> System.out.print(e + " "));
Sequential Stream: 1 2 3 4 5 6 7 8
The following snippet of code shows parallel processing of the stream pipeline because the stream is parallel:
List<Integer> listOfIntegers = List.of(1, 2, 3, 4, 5, 6, 7, 8); System.out.println("Parallel Stream: "); listOfIntegers .stream() .parallel() .forEach(e -> System.out.print(e + " "));
output is (every time new order):
Parallel Stream: 6 5 7 4 2 1 8 3
You could also use this code:
... listOfIntegers .parallelStream() .forEach(e -> System.out.print(e + " ")); ...
Besides being parallel or sequential, a stream also can be ordered or unordered.
Streams may or may not have a defined encounter order. Whether or not a stream has an encounter order
depends on the source and the intermediate operations. Certain stream sources (such as
List or arrays) are
intrinsically ordered, whereas others (such as HashSet) are not. Some intermediate operations, such as
may impose an encounter order on an otherwise unordered stream, and others may render an ordered stream unordered,
BaseStream.unordered(). Further, some terminal operations may ignore encounter order, such as
If a stream is ordered, most operations are constrained to operate on the elements in their encounter order; if
the source of a stream is a
[1, 2, 3], then the result of executing
map(x -> x*2) must be
[2, 4, 6]. However, if the source has no defined encounter order,
then any permutation of the values
[2, 4, 6] would be a valid result.
For sequential streams, the presence or absence of an encounter order does not affect performance, only determinism. If a stream is ordered, repeated execution of identical stream pipelines on an identical source will produce an identical result; if it is not ordered, repeated execution might produce different results.
For parallel streams, relaxing the ordering constraint can sometimes enable more efficient execution. Certain
aggregate operations, such as filtering duplicates (
distinct()) or grouped reductions
Collectors.groupingBy()) can be implemented more efficiently if ordering of elements is not
relevant. Similarly, operations that are intrinsically tied to encounter order, such as
may require buffering to ensure proper ordering, undermining the benefit of parallelism. In cases where the stream
has an encounter order, but the user does not particularly care about that encounter order, explicitly
de-ordering the stream with
unordered() may improve parallel performance for some stateful or
terminal operations. However, most stream pipelines, such as the "sum of weight of blocks" example above,
still parallelize efficiently even under ordering constraints.