In this article, we’ll go through how you can use the Java Stream Reduce method of Stream API.
1. Java Stream Reduce Overview
In general, reduce methods are used to combine multiple values into one result; Java stream.reduce()
does the same. Also, note that unlike map() and filter(), reduce
() is a terminal operation so you cannot apply any additional stream operations.
Before we begin the examples, let’s go through some prerequisite information first.
2. BinaryOperator and BiFunction Overview
2.1 What is a BinaryOperator<T>
BinaryOperator is basically a function that accepts 2 parameters of the same type and returns a value that is the same type as the parameters.
2.2 What is a BiFunction<T, U, R>
BiFunction is a function that accepts a parameter of type T, a parameter of type U and returns a value of type R.
3. BinaryOperator and BiFunction syntax
3.1 One-line Lambda
(a, b) -> c
Both a,b
, and c
must be of the same type when it comes to BinaryOperator
while BiFunction
does not have any restriction unless it is stated explicitly.
Of course, c
should be produced by combining a
and b
using some operations.
3.2 Method Reference
Object_class_::a_method
Assuming that we have a class A,
and a method a_method
inside this class, then method a_method
should have one of the following signatures:
A a_method (A var){ // combine this object with var to produce a new result return result; }
OR
A a_method (A var1, A var2){ // combine var1 and var2 to produce a new result return result; }
Note that Method Reference can always be replaced by one-line lambda while the opposite is not true.
3.3 Multiline Lambda
(variable1, variable2) -> { lines_of_code; ... return result; }
The above will perform some operations before it returns the result. Note that this cannot be replaced by method reference without calling an external method.
4. Java Stream Reduce Examples
Now that we have established how BinaryOperator
and BiFunction
work, let’s explain all three versions of stream.reduce()
method with examples.
4.1 Optional<T> reduce(BinaryOperator<T> accumulator)
This method accepts only a BinaryOperator
parameter and returns an optional of the same type. Consider the following example:
List<String> clh = Arrays.asList("Code", "Learn", "Hub"); List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); String result = clh.stream().reduce(String::concat).get(); //exactly the same as above // String result = clh.stream().reduce((s1, s2) -> s1.concat(s2)).get()); int sum = numbers.stream().reduce(Integer::sum).get(); //exactly the same as above // int sum = numbers.stream().reduce((n1, n2) -> n1 + n2).get(); System.out.println("Reduce on list of strings : " + result); System.out.println("Reduce on list of ints : " + sum);
Let’s take the clh.stream().reduce(String::concat).get();
and go step by step:
First iteration: ("Code", "Learn") -> "Code".concat("Learn")
. This will return “CodeLearn”
Second iteration: ("CodeLearn", "Hub") -> "CodeLearn".concat("Hub")
. This will return “CodeLearnHub”
The result returned will be wrapped as Optional so we use the get()
method to retrieve the underlying value.
The snippet above will print the following:
Reduce on list of strings : CodeLearnHub Reduce on list of ints : 15
4.2 T reduce(T identity, BinaryOperator<T> accumulator)
The difference with the previous method is that:
- It accepts an identity. You can think of identity as the initial value of the reduction
- It does not return an optional but the value itself
Consider this example:
List<String> clh = Arrays.asList("Code", "Learn", "Hub"); List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); String result = clh.stream().reduce("Website:", String::concat); int sum = numbers.stream().reduce(100,Integer::sum); System.out.println("Reduce on list of strings : " + result); System.out.println("Reduce on list of ints : " + sum);
Let’s take the numbers.stream().reduce(100,Integer::sum);
and go step by step:
First iteration: (100, 1) -> 100 + 1
. This will return 101
Second iteration: (101, 2) -> 101 + 2
. This will return 103
Third iteration: (103, 3) -> 103 + 3
. This will return 106
Fourth iteration: (106, 4) -> 106 + 4
. This will return 110
Fifth iteration: (110, 5) -> 110 + 5
. This will return 115
The full output is the following:
Reduce on list of strings : Website:CodeLearnHub Reduce on list of ints : 115
4.3 <U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner)
The difference with the previous method is that it accepts not only a BinaryOperator
function but also a BiFunction
. As a result, this allows us to change the type of the result while with the previous one we couldn’t.
Also note that these:
- value to be returned
- the identity
- the first parameter and the return type of
BiFunction
- The parameters and the return type of the
BinaryOperator
Must have the same type.
Consider an example that will convert eventually a stream of integers to a single result of String type:
List<Integer> clh = Arrays.asList(1, 2, 3); String result = clh.stream() .reduce("String is: ", (firstNumber, secondNumber) -> firstNumber + secondNumber, String::concat ); System.out.println(result);
Let’s go step by step through the process:
First Iteration: ("1", 2) -> "1" + 2
. It returns "12"
Second Iteration: ("12", 3) -> "12" + 3
. It returns "123"
Now that the accumulator function has returned the value, the combiner function takes place and applies the following using the identity as the first parameter and the result of the accumulator function as the second parameter:
Combiner Function: ("String is: ", "123") -> "String is:".concat("123")
. So it returns "String is: 123"
which is the result to be printed.
5. Conclusion
By now you should know exactly how each version of stream.reduce()
works. You can find the source code on our GitHub page.