Parallel vs Sequential Stream in Java
In Java, a stream is a series of objects that can be processed from a data source, such as an array or a collection. It was added to the Java programming language in Java 8, specifically in the java.util.stream package. Streams provide a set of methods that allow for various operations to be performed on the data, enabling programmers to manipulate and transform it according to their requirements.
Streams support a wide range of aggregate operations, including filtering, mapping, limiting, reducing, finding, and matching. These operations allow developers to customize the original data and create a new stream with the desired modifications. Importantly, the original source of the stream remains unaltered, ensuring that the operations performed on the stream do not affect the underlying data source.
1. Parallel Stream
Fortunately, the Java stream library provides convenient and reliable ways to work with parallel streams. One straightforward method is the parallelStream() method, available on the Collection interface. A parallel stream can be easily obtained by invoking this method on a collection.
Another approach is invoking the parallel() method on a sequential stream, part of the BaseStream interface. This method converts a sequential stream into a parallel stream, enabling parallel execution of stream operations. By utilizing these methods and properly managing parallel streams, developers can take advantage of parallel processing in a simple and effective manner, enhancing the performance of their Java applications.
Example 1
In the above code, we have a list of numbers from 1 to 10. We'll calculate the sum of these numbers using both sequential and parallel streams.
FileName: ParallelStreamExample.java
import java.util.Arrays; import java.util.List; public class ParallelStreamExample { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // Sequential Stream int sequentialSum = numbers.stream() .reduce(0, Integer::sum); System.out.println("Sequential sum: " + sequentialSum); // Parallel Stream int parallelSum = numbers.parallelStream() .reduce(0, Integer::sum); System.out.println("Parallel sum: " + parallelSum); } }
Output
Sequential sum: 55 Parallel sum: 55
Example 2
In the above code, we have a list of numbers from 1 to 10. We'll filter out the even numbers from this list using both sequential and parallel streams.
FileName: ParallelStreamExample.java
import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class ParallelStreamExample { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // Sequential Stream List<Integer> sequentialEvenNumbers = numbers.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println("Sequential even numbers: " + sequentialEvenNumbers); // Parallel Stream List<Integer> parallelEvenNumbers = numbers.parallelStream() .filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println("Parallel even numbers: " + parallelEvenNumbers); } }
Output
Sequential even numbers: [2, 4, 6, 8, 10] Parallel even numbers: [2, 4, 6, 8, 10]
2. Sequential Stream
In Java, sequential streams are non-parallel streams that operate on a single thread to process data in a pipeline. By default, if a stream operation is not explicitly specified as parallel, it is treated as a sequential stream. When using a sequential stream, the objects in the stream are processed one by one in a linear fashion on a single processing thread. This means that the stream operations are executed in sequential order, with each element being processed before moving on to the next.
Unlike parallel streams, sequential streams do not take advantage of multi-core systems, even if the underlying system supports parallel execution. They utilize a single thread and do not distribute the workload across multiple cores.
Example 3
In the above code, we have a list of numbers from 1 to 5. We'll use a sequential stream to iterate over the elements and print each number. Using the stream() method, we obtain a sequential stream from the numbers list. The forEach() method is then applied to the stream, where we use the method reference System.out::println to print each element.
FileName: SequentialStreamDemo.java
import java.util.Arrays; import java.util.List; public class SequentialStreamDemo { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // Using stream() method for sequential stream // Iterate and print each element of the stream numbers.stream().forEach(System.out::println); } }
Output
1 2 3 4 5
Example 4
In the above code, we have a list of fruits. We'll use a sequential stream to transform each fruit to uppercase and print them. Using the stream() method, we obtain a sequential stream from the fruits list. Then, the map() operation is applied to the stream, which transforms each element by converting it to uppercase using the method reference String::toUpperCase. Finally, the forEach() method is used to print each transformed fruit on a separate line.
FileName: SequentialStreamDemo.java
import java.util.Arrays; import java.util.List; public class SequentialStreamDemo { public static void main(String[] args) { List<String> fruits = Arrays.asList("Apple", "Banana", "Mango", "Orange", "Grapes"); // Using stream() method for sequential stream // Transform each fruit to uppercase and print fruits.stream() .map(String::toUpperCase) .forEach(System.out::println); } }
Output
APPLE BANANA MANGO ORANGE GRAPES