A parallel stream processes elements using multiple execution threads when permitted. It changes the order in which operations may run, exposing Stream API violations that sequential execution can accidentally conceal. section 9.5
A reduction that fails in parallel
A List hash-code calculation using reduce(1, (x, y) -> x * 31 + y) can appear correct sequentially, even though the accumulator is neither associative nor compatible with the identity value. With parallelStream(), partial results combine in a different order and produce a wrong result. The outcome can also vary with processor count or ForkJoinPool parallelism. section 9.5
Avoid these parallel-stream failure modes:
- Use reductions only when the accumulator is associative and has a valid identity; otherwise, sequential success is not evidence of correctness. section 9.5
- Do not mutate shared collections from intermediate-operation functions; parallel execution can cause exceptions or lost data. section 9.2
- Prefer partitioningBy() for partitioning data, and reserve peek() primarily for debugging because its function may be optimized away. section 9.2
List<String> list = List.of("a", "b", "c", "d");
System.out.println(list.hashCode()); #1
System.out.println(list.stream()
.mapToInt(Objects::hashCode)
.reduce(1, (x, y) -> x * 31 + y)); #2
Compare the standard List hash-code result with the reduce-based implementation that becomes invalid when parallelized. section 9.5
