Java 8函数式编程中’reduce’函数的第三个参数的目的

在什么情况下,在Java 8流中调用’reduce’的第三个参数?

下面的代码尝试遍历字符串列表并添加每个字符串的第一个字符的代码点值。 似乎永远不会使用最终lambda返回的值,如果插入println,它似乎永远不会被调用。 该文档将其描述为“组合器”,但我无法找到更多细节……

int result = data.stream().reduce(0, (total,s) -> total + s.codePointAt(0), (a,b) -> 1000000); 

你在谈论这个function吗?

 reduce  U reduce(U identity, BiFunction accumulator, BinaryOperator combiner) 

使用提供的标识,累积和组合function,对此流的元素进行缩减。 这相当于:

  U result = identity; for (T element : this stream) result = accumulator.apply(result, element) return result; 

但不限于按顺序执行。 标识值必须是组合器函数的标识。 这意味着对于所有u,组合器(identity,u)等于u。 另外,组合器function必须与累加器function兼容; 对于所有你和你,必须具备以下条件:

  combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t) 

这是终端操作。

API注意:使用此表单的许多缩减可以通过map和reduce操作的显式组合更简单地表示。 累加器函数充当融合映射器和累加器,有时比单独的映射和缩减更有效,例如当知道先前减小的值允许您避免某些计算时。 类型参数:U – 结果的类型参数:identity – 组合器函数累加器的标识值 – 用于将附加元素合并到结果组合器中的关联,非干扰,无状态函数 – 关联的,非干扰的,无状态的用于组合两个值的函数,它必须与累加器函数兼容返回:缩减的结果参见:reduce(BinaryOperator),reduce(Object,BinaryOperator)

我假设其目的是允许并行计算,因此我的猜测是它仅在并行执行还原时使用。 如果按顺序执行,则无需使用combiner 。 我肯定不知道这一点 – 我只是根据文档评论猜测“[…]不限于按顺序执行”以及评论中的“并行执行”的许多其他提及。

我认为从java.util.stream包汇总中的Reducing操作段可以回答这个问题。 让我引用这里最重要的部分:


在更一般的forms中,对类型的元素进行reduce操作,产生类型的结果需要三个参数:

  U reduce(U identity, BiFunction accumulator, BinaryOperator combiner); 

这里,identity元素既是缩减的初始种子值,也是没有输入元素的默认结果。 累加器函数获取部分结果和下一个元素,并生成新的部分结果。 组合器function组合了两个部分结果以产生新的部分结果。 (组合器在并行缩减中是必要的,其中输入被分区,为每个分区计算部分累积,然后组合部分结果以产生最终结果。)更正式地,标识值必须是组合器的标识function。 这意味着对于所有ucombiner.apply(identity, u)等于u 。 另外,组合器函数必须是关联的,并且必须与累加器函数兼容:对于所有utcombiner.apply(u, accumulator.apply(identity, t))必须equals()accumulator.apply(u, t)

三参数forms是两参数forms的概括,将映射步骤结合到累积步骤中。 我们可以使用更通用的forms重新构建简单的权重总和示例,如下所示:

  int sumOfWeights = widgets.stream() .reduce(0, (sum, b) -> sum + b.getWeight()) Integer::sum); 

虽然显式的map-reduceforms更具可读性,因此通常应该是首选。 通过将映射和缩减组合成单个函数,可以优化远离重要工作的情况提供通用forms。


换句话说,据我所知,三参数forms在两种情况下很有用:

  1. 并行执行很重要。
  2. 通过组合映射和累积步骤可以实现显着的性能优化。 否则,可以使用更简单和可读的显式map-reduceforms。

之前在同一个文档中提到了显式forms:

 int sumOfWeights = widgets.parallelStream() .filter(b -> b.getColor() == RED) .mapToInt(b -> b.getWeight()) .sum(); 

简单的测试代码,以确认组合器的使用:

 String[] strArray = {"abc", "mno", "xyz"}; List strList = Arrays.asList(strArray); System.out.println("stream test"); int streamResult = strList.stream().reduce( 0, (total,s) -> { System.out.println("accumulator: total[" + total + "] s[" + s + "] s.codePointAt(0)[" + s.codePointAt(0) + "]"); return total + s.codePointAt(0); }, (a,b) -> { System.out.println("combiner: a[" + a + "] b[" + b + "]"); return 1000000;} ); System.out.println("streamResult: " + streamResult); System.out.println("parallelStream test"); int parallelStreamResult = strList.parallelStream().reduce( 0, (total,s) -> { System.out.println("accumulator: total[" + total + "] s[" + s + "] s.codePointAt(0)[" + s.codePointAt(0) + "]"); return total + s.codePointAt(0); }, (a,b) -> { System.out.println("combiner: a[" + a + "] b[" + b + "]"); return 1000000;} ); System.out.println("parallelStreamResult: " + parallelStreamResult); System.out.println("parallelStream test2"); int parallelStreamResult2 = strList.parallelStream().reduce( 0, (total,s) -> { System.out.println("accumulator: total[" + total + "] s[" + s + "] s.codePointAt(0)[" + s.codePointAt(0) + "]"); return total + s.codePointAt(0); }, (a,b) -> { System.out.println("combiner: a[" + a + "] b[" + b + "] a+b[" + (a+b) + "]"); return a+b;} ); System.out.println("parallelStreamResult2: " + parallelStreamResult2); 

输出:

 stream test accumulator: total[0] s[abc] s.codePointAt(0)[97] accumulator: total[97] s[mno] s.codePointAt(0)[109] accumulator: total[206] s[xyz] s.codePointAt(0)[120] streamResult: 326 parallelStream test accumulator: total[0] s[mno] s.codePointAt(0)[109] accumulator: total[0] s[abc] s.codePointAt(0)[97] accumulator: total[0] s[xyz] s.codePointAt(0)[120] combiner: a[109] b[120] combiner: a[97] b[1000000] parallelStreamResult: 1000000 parallelStream test2 accumulator: total[0] s[mno] s.codePointAt(0)[109] accumulator: total[0] s[xyz] s.codePointAt(0)[120] accumulator: total[0] s[abc] s.codePointAt(0)[97] combiner: a[109] b[120] a+b[229] combiner: a[97] b[229] a+b[326] parallelStreamResult2: 326