Java · Collections · Topic 11
Parallel Streams
Use Java parallel streams responsibly by understanding execution, ordering, workload suitability, measurement, and thread-safe collection.
Step 1 of 5
11.1 Introduction
Create a parallel stream directly or convert an existing stream.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ParallelStreamDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> orderedResult = numbers.parallelStream()
.map(number -> number * 10)
.collect(Collectors.toList());
System.out.println("Ordered result: " + orderedResult);
long count = numbers.stream()
.parallel()
.filter(number -> number >= 3)
.count();
System.out.println("Values at least 3: " + count);
}
}Ordered result: [10, 20, 30, 40, 50]
Values at least 3: 3What changes
A parallel stream divides data into smaller parts and may process those parts on multiple CPU cores. The actual threads and scheduling are managed by Java.
numbers.parallelStream();
numbers.stream().parallel();Check your understanding: Which two methods shown here create parallel processing?
parallelStream() and parallel().
Step 2 of 5
11.2 Sequential versus Parallel Stream
Compare the behavior and costs of sequential and parallel processing.
| Sequential stream | Parallel stream |
|---|---|
| Uses one processing flow | May use multiple worker threads |
| Usually preserves encounter order | forEach() may not preserve encounter order |
| Easier to understand and debug | More difficult to debug |
| Often suitable for small collections | May help with large CPU-intensive data |
| Lower coordination overhead | Adds splitting and coordination overhead |
import java.util.Arrays;
import java.util.List;
public class SequentialParallelComparisonDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50);
int sequentialSum = numbers.stream()
.mapToInt(Integer::intValue)
.sum();
int parallelSum = numbers.parallelStream()
.mapToInt(Integer::intValue)
.sum();
System.out.println("Sequential sum: " + sequentialSum);
System.out.println("Parallel sum: " + parallelSum);
}
}Sequential sum: 150
Parallel sum: 150Check your understanding: Which stream style is normally easier to understand and debug?
A sequential stream.
Step 3 of 5
11.3 Ordering
Distinguish forEach() from forEachOrdered() on an ordered parallel stream.
import java.util.Arrays;
import java.util.List;
public class ParallelOrderingDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
System.out.println("Ordered output:");
numbers.parallelStream()
.forEachOrdered(System.out::println);
}
}Ordered output:
1
2
3
4
5numbers.parallelStream()
.forEach(System.out::println);Check your understanding: Which terminal operation preserves encounter order?
forEachOrdered().
Step 4 of 5
11.4 When Parallel Streams May Be Useful
Evaluate workload characteristics before enabling parallel execution.
| Parallel streams may help when | Prefer sequential streams when |
|---|---|
| The data set is large | The collection is small |
| Each element needs substantial CPU work | Each operation is very simple |
| Operations are independent | Tasks mainly wait for input or output |
| No shared mutable data is used | Processing changes shared state |
| Strict encounter order is unnecessary | Strict order must be preserved |
| A benchmark shows improvement | Performance has not been measured |
Decision checklist
- Is the work CPU-bound?
- Is the data set large enough to offset overhead?
- Are element operations independent?
- Is the reduction associative?
- Can the pipeline avoid shared mutable state?
- Does measurement show a real improvement?
Check your understanding: Should a small collection with trivial operations automatically use a parallel stream?
No.
Step 5 of 5
11.5 Avoid Shared Mutable Data
Replace unsafe side effects with stream collection operations.
List<Integer> result = new ArrayList<>();
numbers.parallelStream()
.filter(number -> number % 2 == 0)
.forEach(result::add);import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ParallelCollectionDemo {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
List<Integer> result = numbers.parallelStream()
.filter(number -> number % 2 == 0)
.collect(Collectors.toList());
System.out.println("Even numbers: " + result);
}
}Even numbers: [2, 4, 6, 8]Why collect() is safer
Collectors combine partial results using stream-aware accumulation rather than exposing one ArrayList to concurrent writes.
Check your understanding: Why is result::add unsafe when result is a shared ArrayList?
Multiple worker threads may modify the non-thread-safe list concurrently.