Definition
- sorted() establishes an encounter order on the stream.
- forEach in parallel invokes the action as threads finish, ignoring encounter order.
- Output is jumbled despite the data being sorted internally.
- forEachOrdered preserves encounter order by coordinating threads.
- Alternative: collect into a
List(preserves order) and iterate sequentially.
At National Bank of India, Priya sorts the transaction slips into order, but then hands them to many clerks who each shout out their slip as soon as they finish. The shouts overlap and come out jumbled. To hear them in order, she must use the ordered reader.
Understanding the Concepts
What is the problem?
sorted() establishes an encounter order: internally, the stream knows the correct order. But forEach is explicitly documented to not guarantee any order in parallel: it invokes the action as each thread finishes an element. So the prints interleave unpredictably even though the data is sorted.
How do you demonstrate it?
package com.bank.core;
import java.util.List;
public class SortedForEachParallel {
public static void main(String[] args) {
List<Integer> numbers = List.of(5, 3, 1, 4, 2);
System.out.println("--- sorted().forEach (parallel) ---");
numbers.parallelStream()
.sorted()
.forEach(n -> System.out.print(n + " "));
System.out.println();
System.out.println("--- sorted().forEachOrdered (parallel) ---");
numbers.parallelStream()
.sorted()
.forEachOrdered(n -> System.out.print(n + " "));
System.out.println();
}
}
--- sorted().forEach (parallel) ---
3 1 4 2 5
--- sorted().forEachOrdered (parallel) ---
1 2 3 4 5
With forEach, the output is scrambled. With forEachOrdered, it comes out in sorted order.
Why does sequential work but parallel does not?
In sequential mode, there is only one thread, so forEach processes elements one by one in encounter order. In parallel, multiple threads process chunks concurrently, and forEach outputs as each thread finishes, ignoring the encounter order.
What is the difference between encounter order and consumption order?
| Concept | Meaning |
|---|---|
| Encounter order | The logical order of elements in the stream (set by source or sorted) |
| Consumption order | The order in which forEach actually processes elements |
| Sequential | Consumption matches encounter order |
| Parallel forEach | Consumption is arbitrary |
| Parallel forEachOrdered | Consumption matches encounter order |
What are common mistakes?
Mistake: Assuming sorted means ordered output in parallel.
// WRONG: sorted output in parallel is not guaranteed with forEach
numbers.parallelStream()
.sorted()
.forEach(System.out::println); // jumbled
// CORRECT: use forEachOrdered for ordered output
numbers.parallelStream()
.sorted()
.forEachOrdered(System.out::println); // in order
// ALTERNATIVE: collect then iterate
List<Integer> sorted = numbers.parallelStream().sorted().toList();
sorted.forEach(System.out::println); // in order
What about the collect alternative?
collect(toList()) preserves encounter order even in parallel. You can then iterate the list sequentially.
List<Integer> sorted = numbers.parallelStream().sorted().toList();
sorted.forEach(System.out::println); // always in sorted order
What should a senior engineer know?
Distinguish the stream's encounter order from the terminal's consumption order. sorted sets the encounter order, but only order-respecting terminals (forEachOrdered, collect, toArray) preserve it in the output under parallelism. Plain forEach trades ordering for throughput.
What are common interview follow-up questions?
- "Why does sequential forEach preserve order?" Only one thread, so it processes in encounter order by default.
- "Does forEachOrdered negate parallelism?" Partially: computation is parallel, but emission is sequential.
- "What is the best alternative?" Collect into a list, which preserves order, then iterate.
Visual Overview
sorted(): [1, 2, 3, 4, 5] ← encounter order established
forEach (parallel): forEachOrdered (parallel):
┌──────────────────────┐ ┌──────────────────────┐
│ Thread-1: prints 3 │ │ Wait for order... │
│ Thread-2: prints 1 │ │ Emit 1 │
│ Thread-3: prints 4 │ │ Emit 2 │
│ Thread-1: prints 2 │ │ Emit 3 │
│ Thread-2: prints 5 │ │ Emit 4 │
│ Output: 3 1 4 2 5 │ │ Emit 5 │
│ ← JUMBLED │ │ Output: 1 2 3 4 5 │
└──────────────────────┘ │ ← ORDERED │
└──────────────────────┘
Key Takeaway
The sort and the printing are two different things. sorted() establishes an encounter order on the stream, but forEach is explicitly documented to give no ordering guarantee in parallel. It invokes its action as each thread finishes, so the output is jumbled even though the data is sorted internally.
At the National Bank of India, a developer sorted transactions and printed them with forEach, and in parallel the output came out scrambled. The fix is forEachOrdered, which coordinates threads to emit in encounter order, or collecting into a list and iterating sequentially.
The key insight is distinguishing the stream's encounter order from the terminal's consumption order. sorted sets the encounter order, but only order-respecting terminals preserve it in parallel. Plain forEach trades ordering for throughput.
Remember one sentence: sorted() sets encounter order but parallel forEach ignores it; use forEachOrdered or collect to a List for ordered output.
---