Parallel Streams in Java
Introduction to Parallel Streams
Parallel streams in Java 8 allow you to leverage multi-core processors by dividing the
workload across multiple threads. This can significantly improve performance for large data
sets or computationally intensive tasks.
How to Create Parallel Streams
From Collections:
List<String> list = Arrays.asList("a", "b", "c", "d");
list.parallelStream().forEach(System.out::println);
From Existing Streams:
Stream<String> stream = Stream.of("a", "b", "c", "d");
stream.parallel().forEach(System.out::println);
Example: Processing a Stream in Parallel
Here's a simple example that demonstrates how to use a parallel stream to process a list of
integers:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int sum = numbers.parallelStream()
.mapToInt(Integer::intValue)
.sum();
System.out.println("Sum: " + sum); // Output: Sum: 55
Performance Considerations
While parallel streams can improve performance, they are not always the best choice. Here
are some factors to consider:
1. Overhead: Parallel streams introduce overhead due to thread management and context
switching.
2. Task Size: For small tasks, the overhead may outweigh the benefits.
3. Data Source: Some data sources (like ArrayList) are more efficient with parallel streams
than others (like LinkedList).
4. Thread Safety: Ensure that the operations on the stream are thread-safe.
5. Combining Results: Operations that require combining results (e.g., reduce) should be
efficient and associative.
Example: Using Parallel Streams with Collectors
Parallel streams work seamlessly with collectors. Here's an example of grouping elements
in parallel:
List<String> words = Arrays.asList("apple", "banana", "cherry", "date", "elderberry", "fig",
"grape");
Map<Integer, List<String>> groupedByLength = words.parallelStream()
.collect(Collectors.groupingBy(String::length));
System.out.println(groupedByLength);
// Output: {3=[fig], 4=[date], 5=[apple, grape], 6=[banana, cherry], 10=[elderberry]}
Example: Custom Parallel Processing
You can use parallel streams for more complex tasks, such as performing a custom
reduction:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int product = numbers.parallelStream()
.reduce(1, (a, b) -> a * b, (a, b) -> a * b);
System.out.println("Product: " + product); // Output: Product: 120
Thread-Safety Considerations
When using parallel streams, make sure that operations are thread-safe. Avoid modifying
shared mutable state. For example, this is NOT thread-safe:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> result = new ArrayList<>();
numbers.parallelStream().forEach(result::add); // Unsafe
Instead, use thread-safe data structures or collectors:
List<Integer> result = numbers.parallelStream()
.collect(Collectors.toList()); // Safe
Summary
Parallel streams in Java provide a powerful and easy-to-use mechanism for parallel
processing, making it easier to write concurrent code. However, it's important to consider
the overhead, data source, and thread safety to ensure optimal performance and
correctness.