Stream. duce
有3个方法重载。
reduce(BinaryOperator<T> accumulator)
reduce(T identity, BinaryOperator<T> accumulator)
reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
我很难理解第三重载(Stream. duce(标识、累加器、组合器)
)是如何工作的,以及它的用例是什么。那么,它是如何工作的,为什么会存在呢?
如果我理解正确,您的问题是关于第三个参数组合器
。
首先,Java的目标之一是为顺序流和并行流提供类似的API。duce
的3参数版本对并行流很有用。
假设您从Collection的值减少
基本上,它将映射函数与约简结合在一起。我看到的大多数示例都没有真正说明为什么在不同的步骤中调用map()
和普通的duce()
更可取。API注释在这里派上了用场:
使用这种形式的许多约简可以通过map
和duce
操作的显式组合来更简单地表示。累加器
函数充当融合映射器和累加器,有时比单独的映射和约简更有效,例如当知道之前的约简值时可以让您避免一些计算。
假设我们有一个流
BigDecimal product = numbers.map(BigDecimal::new)
.reduce(BigDecimal.ONE, BigDecimal::multiply);
但这是低效的。如果其中一个数字是“0”,我们将余数转换为BigDecimal
是在浪费周期。我们可以在这里使用3-argduce()
来绕过映射逻辑:
BigDecimal product = numbers.reduce(BigDecimal.ONE,
(d, n) -> d.equals(BigDecimal.ZERO) ? BigDecimal.ZERO : new BigDecimal(n).multiply(d),
BigDecimal::multiply);
当然,完全短路流会更有效,但在流中这很棘手,尤其是在并行情况下。这只是一个让这个概念得到理解的例子。
注意:一些示例是为演示而设计的。在某些情况下,可以使用简单的. sum()
。
imo,最大的区别在于第三种形式有一个BiFunction
作为第二个参数,而不是BinaryOperator
。因此您可以使用第三种形式来更改结果类型。它还有一个BinaryOperator
作为组合器,以组合来自并行操作的不同结果。
生成一些数据
record Data(String name, int value) {}
Random r = new Random();
List<Data> dataList = r.ints(1000, 1, 20).mapToObj(i->new Data("Item"+i, i)).toList();
没有并行操作,但类型不同。但需要第三个参数,因此只需返回总和。
int sum = dataList.stream().reduce(0, (item, data) -> item + data.value,
(finalSum, partialSum) -> finalSum);
System.out.println(sum);
指纹
10162
第二种形式。使用map获取要求和的值。BinaryOperator
这里使用,因为类型是相同的,没有并行操作。
sum = dataList.stream().map(Data::value).reduce(0, (sum1,val)->sum1+val);
System.out.println(sum); // print same as above
这与上面显示的相同,但是并行的。第三个参数累加部分和。这些总和在下一个线程完成时累加,因此输出可能没有合理的顺序。
sum = dataList.parallelStream().reduce(0, (sum1, data) -> sum1 + data.value,
(finalSum, partialSum) -> {
System.out.println("Adding " + partialSum + " to " + finalSum);
finalSum += partialSum;
return finalSum;
});
System.out.println(sum);
打印如下内容
Adding 586 to 670
Adding 567 to 553
Adding 1256 to 1120
Adding 715 to 620
Adding 624 to 601
Adding 1335 to 1225
Adding 2560 to 2376
Adding 662 to 579
Adding 706 to 715
Adding 1421 to 1241
Adding 713 to 689
Adding 576 to 586
Adding 1402 to 1162
Adding 2662 to 2564
Adding 4936 to 5226
10162
最后一个注意事项。Collectors.减少
方法都没有BiFunction
来处理不同的类型。为了处理这个问题,第二个参数是一个Function
来充当映射器,因此第三个参数,一个BinaryOperator
可以收集值。
sum = dataList.parallelStream().collect(
Collectors.reducing(0, Data::value, (finalSum, partialSum) -> {
System.out.println(
"Adding " + partialSum + " to " + finalSum);
return finalSum + partialSum;
}));
System.out.println(sum);