使用reduce


map()filter()都是Stream的转换方法,而Stream.reduce()则是Stream的一个聚合方法,它可以把一个Stream的所有元素按照聚合函数聚合成一个结果。

我们来看一个简单的聚合方法:

使用reduce - 图1

reduce()方法传入的对象是BinaryOperator接口,它定义了一个apply()方法,负责把上次累加的结果和本次的元素进行运算,并返回累加的结果:

  1. @FunctionalInterface
  2. public interface BinaryOperator<T> {
  3. // Bi操作:两个输入,一个输出
  4. T apply(T t, T u);
  5. }

上述代码看上去不好理解,但我们用for循环改写一下,就容易理解了:

  1. Stream<Integer> stream = ...
  2. int sum = 0;
  3. for (n : stream) {
  4. sum = (sum, n) -> sum + n;
  5. }

可见,reduce()操作首先初始化结果为指定值(这里是0),紧接着,reduce()对每个元素依次调用(acc, n) -> acc + n,其中,acc是上次计算的结果:

  1. // 计算过程:
  2. acc = 0 // 初始化为指定值
  3. acc = acc + n = 0 + 1 = 1 // n = 1
  4. acc = acc + n = 1 + 2 = 3 // n = 2
  5. acc = acc + n = 3 + 3 = 6 // n = 3
  6. acc = acc + n = 6 + 4 = 10 // n = 4
  7. acc = acc + n = 10 + 5 = 15 // n = 5
  8. acc = acc + n = 15 + 6 = 21 // n = 6
  9. acc = acc + n = 21 + 7 = 28 // n = 7
  10. acc = acc + n = 28 + 8 = 36 // n = 8
  11. acc = acc + n = 36 + 9 = 45 // n = 9

因此,实际上这个reduce()操作是一个求和。

如果去掉初始值,我们会得到一个Optional<Integer>

  1. Optional<Integer> opt = stream.reduce((acc, n) -> acc + n);
  2. if (opt.isPresent) {
  3. System.out.println(opt.get());
  4. }

这是因为Stream的元素有可能是0个,这样就没法调用reduce()的聚合函数了,因此返回Optional对象,需要进一步判断结果是否存在。

利用reduce(),我们可以把求和改成求积,代码也十分简单:

使用reduce - 图2

注意:计算求积时,初始值必须设置为1

除了可以对数值进行累积计算外,灵活运用reduce()也可以对Java对象进行操作。下面的代码演示了如何将配置文件的每一行配置通过map()reduce()操作聚合成一个Map<String, String>

使用reduce - 图3

小结

reduce()方法将一个Stream的每个元素依次作用于BinaryOperator,并将结果合并。

reduce()是聚合方法,聚合方法会立刻对Stream进行计算。

读后有收获可以支付宝请作者喝咖啡,读后有疑问请加微信群讨论:

使用reduce - 图4使用reduce - 图5