map()
和filter()
都是Stream
的转换方法,而Stream.reduce()
则是Stream
的一个聚合方法,它可以把一个Stream
的所有元素按照聚合函数聚合成一个结果。
我们来看一个简单的聚合方法:
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 } }
reduce()
方法传入的对象是BinaryOperator
接口,它定义了一个apply()
方法,负责把上次累加的结果和本次的元素
进行运算,并返回累加的结果:
@FunctionalInterfacepublic interface BinaryOperator<T> { // Bi操作:两个输入,一个输出 T apply(T t, T u); }
上述代码看上去不好理解,但我们用for
循环改写一下,就容易理解了:
Stream<Integer> stream = ... int sum = 0; for (n : stream) { sum = (sum, n) -> sum + n; }
可见,reduce()
操作首先初始化结果为指定值(这里是0),紧接着,reduce()
对每个元素依次调用(acc, n) -> acc + n
,其中,acc
是上次计算的结果:
// 计算过程:acc = 0 // 初始化为指定值acc = acc + n = 0 + 1 = 1 // n = 1acc = acc + n = 1 + 2 = 3 // n = 2acc = acc + n = 3 + 3 = 6 // n = 3acc = acc + n = 6 + 4 = 10 // n = 4acc = acc + n = 10 + 5 = 15 // n = 5acc = acc + n = 15 + 6 = 21 // n = 6acc = acc + n = 21 + 7 = 28 // n = 7acc = acc + n = 28 + 8 = 36 // n = 8acc = acc + n = 36 + 9 = 45 // n = 9
因此,实际上这个reduce()
操作是一个求和。
如果去掉初始值,我们会得到一个Optional<Integer>
:
Optional<Integer> opt = stream.reduce((acc, n) -> acc + n); if (opt.isPresent) { System.out.println(opt.get()); }
这是因为Stream
的元素有可能是0个,这样就没法调用reduce()
的聚合函数了,因此返回Optional
对象,需要进一步判断结果是否存在。
利用reduce(),我们可以把求和改成求积,代码也十分简单:
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 } }
注意:计算求积时,初始值必须设置为1
。
除了可以对数值进行累积计算外,灵活运用reduce()
也可以对Java对象进行操作。下面的代码演示了如何将配置文件的每一行配置通过map()
和reduce()
操作聚合成一个Map<String, String>
: