Appearance
Using reduce
Both map()
and filter()
are transformation methods in Stream
, whereas Stream.reduce()
is an aggregation method that aggregates all elements of a Stream
into a single result according to a given aggregation function.
Let's look at a simple aggregation example:
java
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
int sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(0, (acc, n) -> acc + n);
System.out.println(sum); // 45
}
}
The reduce()
method accepts an object of the BinaryOperator
interface, which defines an apply()
method responsible for performing an operation on the accumulated result and the current element, and then returning the accumulated result:
java
@FunctionalInterface
public interface BinaryOperator<T> {
// Binary operation: two inputs, one output
T apply(T t, T u);
}
The code above may seem difficult to understand, but rewriting it using a for
loop makes it easier:
java
Stream<Integer> stream = ...
int sum = 0;
for (n : stream) {
sum = (sum, n) -> sum + n;
}
As we can see, the reduce()
operation first initializes the result to a specified value (in this case, 0), then reduce()
calls (acc, n) -> acc + n
for each element sequentially, where acc
is the accumulated result from the previous calculation:
// Calculation process:
acc = 0 // Initialize to the specified value
acc = acc + n = 0 + 1 = 1 // n = 1
acc = acc + n = 1 + 2 = 3 // n = 2
acc = acc + n = 3 + 3 = 6 // n = 3
acc = acc + n = 6 + 4 = 10 // n = 4
acc = acc + n = 10 + 5 = 15 // n = 5
acc = acc + n = 15 + 6 = 21 // n = 6
acc = acc + n = 21 + 7 = 28 // n = 7
acc = acc + n = 28 + 8 = 36 // n = 8
acc = acc + n = 36 + 9 = 45 // n = 9
Thus, this reduce()
operation is essentially a summation.
If we remove the initial value, we get an Optional<Integer>
:
java
Optional<Integer> opt = stream.reduce((acc, n) -> acc + n);
if (opt.isPresent()) {
System.out.println(opt.get());
}
This is because there could be zero elements in the Stream
, making it impossible to call the aggregation function of reduce()
, hence returning an Optional
object that requires further checking for the presence of a result.
By using reduce()
, we can change the summation to a product calculation, and the code is just as simple:
java
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
int s = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(1, (acc, n) -> acc * n);
System.out.println(s); // 362880
}
}
Note: When calculating the product, the initial value must be set to 1.
In addition to numerical accumulation, the flexible use of reduce()
can also operate on Java objects. The following code demonstrates how to aggregate each line of configuration from a configuration file into a Map<String, String>
using map()
and reduce()
:
java
import java.util.*;
public class Main {
public static void main(String[] args) {
// Read configuration file line by line:
List<String> props = List.of("profile=native", "debug=true", "logging=warn", "interval=500");
Map<String, String> map = props.stream()
// Convert k=v to Map[k]=v:
.map(kv -> {
String[] ss = kv.split("\\=", 2);
return Map.of(ss[0], ss[1]);
})
// Aggregate all Maps into one Map:
.reduce(new HashMap<String, String>(), (m, kv) -> {
m.putAll(kv);
return m;
});
// Print results:
map.forEach((k, v) -> {
System.out.println(k + " = " + v);
});
}
}
Summary
The reduce()
method applies a BinaryOperator
to each element of a Stream
sequentially and merges the results. It is an aggregation method that performs computation on the Stream
immediately.