The terminal operation in a stream pipeline defines how we want to produce/generate a result, or how do we want to define a final process for each element in our resulting Stream
.
In this section we’ll cover some of the most common types of terminal operations provided by Java.
Accumulating Collections
An usual desired result is to retrieve the elements from a Stream<T>
and put them in a List<T>
, Set<T>
, or Map<K,V>
either for further processing or to return them to a calling method.
To achieve this, we can use the collect
terminal operation. This method allows us to provide a Collector
that we can use to accumulate the elements of the stream.
Java provides several implementations out of the box in the Collectors utility class, like Collectors.toList()
that will place all resulting elements from the Stream<T>
into a List<T>
.
Let’s look at some code using the same example we introduced before:
In the example code run the JavaBasicIntegerStreamApp
|
Integer[] array = { 1, 2, 4, 5 };
List<Integer> result = Stream.of(array)
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
for (int number : result) {
System.out.println(number);
}
Output:
2
4
In this particular example, as we’re using the Collectors.toList()
collector, our result
is expected to be a List<Integer>
.
In a similar way you can use Collectors.toSet()
to get the result as a Set<T>
data structure. There are more predefined Collectors
offered that will be covered in a separate course.
Reduce
A reduction is the process of combining/processing all the elements to generate an accumulated result. To continue with our example Integer
stream, a reduction operation would be to return the sum of all the elements, or to find the maximum, or the standard deviation, among many others.
The Stream<T>
interface defines a set of reduce
methods for this purpose. For example, to perform a sum
reduction we could do the following:
In the example code run the JavaBasicStreamReduceApp
|
Integer[] array = { 1, 2, 3 };
Integer sum = Stream.of(array)
.reduce(0, (num1, num2) -> num1 + num2);
System.out.println("The sum is: " + sum);
Output:
The sum is: 6
The reduce method behind the scenes will invoke the operation (in our case a sum num1 + num2
) for each element in the stream and accumulate the result. The next table summarizes the internal steps for this example:
Step | Current Result | Next Element | Operation | New Result |
---|---|---|---|---|
1 |
|
|
|
|
2 |
|
|
|
|
3 |
|
|
|
|
The Current Result
starts with a value of 0
as that’s the initial value/identity we’re passing to the reduce
method as the first parameter.
Special cases of reductions
The Stream
API comes with a set of predefined reductions that cover some common cases. These special cases include:
Operation | Description |
---|---|
|
Returns the number of elements in the stream. |
|
These methods return the maximum/minimum element from a stream respectively. Both methods receive a |
If you take a look at the primitive streams, like IntStream
, they also define additional cases of reduction out of the box:
Operation | Description |
---|---|
|
Returns the number of elements in the stream. |
|
These methods return the maximum/minimum element from a stream respectively. |
|
Returns the sum of the elements in the stream. |
|
Returns the average of the elements in the stream. |
For example, we could do the sum
example above using the special reduction case:
In the example code run the JavaBasicIntStreamSumApp
|
int[] array = { 1, 2, 3 };
Integer sum = IntStream.of(array).sum();
System.out.println("The sum is: " + sum);
Output:
The sum is: 6
Execute an Action per element
In some scenarios we might not want to collect/reduce our elements, but instead we want to execute a specific action for each resulting element in our stream pipeline.
To do this, we make use of the forEach
method in the Stream
API. The forEach
method receives as a parameter the action we want to execute per element.
For example, lets assume we want to print out all the elements in our Integer
stream after multiplying each element by 2:
In the example code run the JavaBasicIntegerStreamTimesTwoApp
|
Integer[] array = { 1, 2, 4, 5 };
Stream.of(array)
.map(n -> n * 2)
.forEach(n -> System.out.println(n));
Output:
2
4
8
10
More terminal operations
There are more terminal operations than the ones we cover in this section. We’ll cover more intermediate and terminal operations in the remainder of this course and in separate courses.