带有3个参数的Java8 stream.reduce() – 获得透明度

我编写了这段代码,将一个单词列表缩减为多少单词以’A’开头。 我只是为了学习Java 8而写它,所以我想更好地理解它[免责声明:我意识到这可能不是编写这段代码的最好方法; 这只是为了练习!]

Long countOfAWords = results.stream().reduce( 0L, (a, b) -> b.charAt(0) == 'A' ? a + 1 : a, Long::sum); 

中间参数/ lambda(称为累加器)似乎能够在没有最终’Combiner’参数的情况下减少完整列表。 事实上,Javadoc实际上说:

{@code accumulator}函数充当融合映射器和累加器*,它有时比单独的映射和缩减更有效,*例如当知道先前减少的值允许您避免*某些计算时。

[编辑来自作者] –以下陈述是错误的,所以不要让它混淆你; 我只是把它放在这里,所以我不会破坏答案的原始背景。

无论如何,我可以推断累加器必须输出组合器组合的1和0。 我没有从文档中发现这一点特别明显。

我的问题

有没有办法在组合器执行之前查看输出是什么,所以我可以看到组合器组合的1和0的列表? 这将有助于调试更复杂的情况,我相信我最终会遇到这种情况。

组合器不会减少0和1的列表。 当流不是并行运行时,在这种情况下不使用它,因此以下循环是等效的:

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

并行运行流时,任务将跨越多个线程。 因此,例如,管道中的数据被划分为块,其独立地评估和产生结果。 然后组合器用于合并该结果。

因此,您将看不到减少的列表,而是看到2个值,即标识值或由求和的任务计算的另一个值。 例如,如果在组合器中添加print语句

 (i1, i2) -> {System.out.println("Merging: "+i1+"-"+i2); return i1+i2;}); 

你可以看到这样的东西:

 Merging: 0-0 Merging: 0-0 Merging: 1-0 Merging: 1-0 Merging: 1-1 

这将有助于调试更复杂的情况,我相信我会偶然遇到。

更一般地说,如果你想在移动中看到管道上的数据,你可以使用peek (或调试器也可以帮助)。 所以适用于你的例子:

 long countOfAWords = result.stream().map(s -> s.charAt(0) == 'A' ? 1 : 0).peek(System.out::print).mapToLong(l -> l).sum(); 

哪个可以输出:

 100100 

[免责声明:我意识到这可能不是编写此代码的最佳方式; 这只是为了练习!]。

实现任务的惯用方法是filter流,然后简单地使用count

 long countOfAWords = result.stream().filter(s -> s.charAt(0) == 'A').count(); 

希望能帮助到你! 🙂

查看正在发生的事情的一种方法是用包含println的lambda替换方法引用Long::sum

 List results = Arrays.asList("A", "B", "A", "A", "C", "A", "A"); Long countOfAWords = results.stream().reduce( 0L, (a, b) -> b.charAt(0) == 'A' ? a + 1 : a, (a, b) -> { System.out.println(a + " " + b); return Long.sum(a, b); }); 

在这种情况下,我们可以看到实际上没有使用组合器。 这是因为流不是平行的。 我们所做的只是使用累加器将每个String与当前的Long结果相继组合; 没有两个Long值组合在一起。

如果通过parallelStream替换stream ,您可以看到使用了组合器并查看它组合的值。