While dealing with collections in Java, you can often face the challenge of reducing all collection elements to a single result. The example of such a problem is finding a bank account with the maximum amount of money among the collection of accounts or reducing account transaction values to the total amount of transferred money. Java Stream API provides several terminal operations that allow us to solve this problem in a functional way. These operations combine stream elements and return a single value.
The reduce operation
reduce is a method of a
Stream class that combines elements of a stream into a single value. The result can be a value of a primitive type or a complex object.
reduceis a terminal operation, which means that it begins all evaluations with the stream and produces a final result.
Let’s consider two of the most common uses of the Java Stream API
1) In the simplest case, the
reduce method accepts a two-argument accumulator function. The first argument of the accumulator is a partial result of the reduction, while the second one is the next element of a stream. The accumulator should return a reduction value that will be assigned to the partial result. Let’s consider the following list of transactional values:
List<Integer> transactions = List.of(20, 40, -60, 5);
Now we sum up all transaction values to get the total amount of transferred money by using
transactions.stream().reduce((sum, transaction) -> sum + transaction);
At the first iteration of the reduction, the
sum argument equals to the first element of the stream whose value is 20. The
transaction argument represents the next element of the stream whose value is 40. After the first iteration, the
sum accumulates the current value of the
transaction argument and will be equal to 20 + 40 = 60. In the table below you can see the values of
transaction arguments on each iteration:
reduceoperation that accepts the accumulator function returns
Optionaltype. In the example above the
reducemethod returns a container
Optional<Integer>that contains an
Integervalue of 5.
reduce implementation has one additional parameter: identity value or seed. The identity value represents the initial value for the reduction operation. Let’s rewrite our previous example using
transactions.stream().reduce(0, (sum, transaction) -> sum + transaction);
Now, the initial value of the partial result
sum is 0 and the initial value of the
transaction element is 20.
Note, that if a
reducemethod accepts both identity value and accumulator function, it will return a primitive type or an object, but not an
Optionalcontainer. If the stream is empty, the
reduceoperation will return identity value.
Other reduction operations
reduce is a generally purposed operation, Stream API provides many specific reduction operations such as
max, etc. Similar to the
reduce operation they are terminal operations that produce a single value. Let’s consider an example with a generic stream, assuming that we need to find the maximum value from the list of given balances. We can do it by using reduce operation:
transactions.stream().reduce((t1, t2) -> t2 > t1 ? t2 : t1)
The code above compares a partial result
t1 with the next element of the stream
t2 and assigns the value of
t2 is numerically greater than
t1. We can get the same result in a more elegant way using the
As you may notice the method
max of the
Stream<T> class accepts a comparison function in order to find a maximum element among stream elements since we need to specify how exactly to compare generic type. Unlike the generic streams, primitive-specialized streams such as
LongStream already “know” the type of their elements. That is why their
min functions do not require any comparison function:
IntStream.of(20, 40, -60, 5).max();
Because of type awareness, primitive-specialized streams have numerically specialized functions such as
sum, which cannot be provided by generic streams.
reduce is a terminal operation that produces a single value result by combining stream elements. It can accept the accumulator function only or in combination with identity value. The accumulator function takes two arguments: partial result and the next element of a stream. If the identity value is provided, the initial value of the partial result will be equal to that value. Otherwise, the partial result’s initial value will be equal to the first element of a stream. Besides the
reduce, primitive-specialized streams provide more reduction operations that depend on the numeric type.