我认为java.util.stream包摘要中的Reduction operations段可以回答这个问题。让我在这里引用最重要的部分:
在更一般的形式中,对 <T> 类型的元素进行归约操作并产生 <U> 类型的结果需要三个参数:
<U> U reduce(U identity,
BiFunction<U, ? super T, U> accumulator,
BinaryOperator<U> combiner);
这里,标识元素既是归约的初始种子值,也是没有输入元素时的默认结果。累加器函数获取部分结果和下一个元素,并产生新的部分结果。 combiner 函数组合两个部分结果以产生新的部分结果。 (组合器在并行缩减中是必需的,其中输入被分区,为每个分区计算部分累积,然后将部分结果组合以产生最终结果。)
更正式地说,标识值必须是组合器函数的标识。这意味着对于所有u,combiner.apply(identity, u) 等于u。此外,combiner 函数必须是关联函数,并且必须与 accumulator 函数兼容:对于所有 u 和 t,combiner.apply(u, accumulator.apply(identity, t)) 必须是 equals() 到 accumulator.apply(u, t)。
三参数形式是二参数形式的推广,在累加步骤中加入了映射步骤。我们可以使用更一般的形式重新构建简单的权重总和示例,如下所示:
int sumOfWeights = widgets.stream()
.reduce(0,
(sum, b) -> sum + b.getWeight())
Integer::sum);
虽然显式 map-reduce 形式更具可读性,因此通常应该是首选。广义形式适用于可以通过将映射和归约组合为单个函数来优化重要工作的情况。
换句话说,据我了解,三参数形式在两种情况下很有用:
- 当并行执行很重要时。
- 当可以通过结合映射和累积步骤来实现显着的性能优化时。否则,可以使用更简单易读的显式 map-reduce 形式。
之前在同一个文档中提到了显式形式:
int sumOfWeights = widgets.parallelStream()
.filter(b -> b.getColor() == RED)
.mapToInt(b -> b.getWeight())
.sum();