具有 3 个参数的 Java8 stream.reduce() - 获得透明度
我编写这段代码是为了将单词列表减少为有多少单词以A"开头的长计数.我只是为了学习 Java 8 而编写它,所以我想更好地理解它[免责声明:我意识到这可能不是编写此代码的最佳方式;这只是为了练习!].
Long countOfAWords = results.stream().reduce(0L,(a, b) ->b.charAt(0) == 'A' ?一+1:一,长::总和);
中间参数/lambda(称为累加器)似乎能够在没有最终Combiner"参数的情况下减少完整列表.事实上,Javadoc 实际上是这样说的:
<块引用>{@code accumulator} 函数充当一个融合映射器和累加器,* 有时比单独的映射和归约更有效,*例如当知道先前减少的值时,您可以避免* 一些计算.
[Edit From Author] - 下面的说法是错误的,所以不要让它混淆你;我只是将其保留在这里,以免破坏答案的原始上下文.
无论如何,我可以推断出累加器一定只是输出组合器组合的 1 和 0.不过,我并没有从文档中发现这一点特别明显.
我的问题
有没有办法在组合器执行之前查看输出是什么,以便我可以看到组合器组合的 1 和 0 的列表?这将有助于调试更复杂的情况,我相信我最终会遇到.
解决方案combiner 不会减少 0 和 1 的列表.当流不是并行运行时,在这种情况下不使用它,因此以下循环是等效的:
U 结果 = 身份;for (T element : 这个流)结果 = accumulator.apply(结果,元素)返回结果;
当您并行运行流时,任务会跨入多个线程.例如,管道中的数据被划分为独立评估和产生结果的块.然后使用组合器来合并这个结果.
因此,您不会看到减少的列表,而是 2 个值,要么是标识值,要么与由任务计算的另一个值相加.例如,如果您在组合器中添加打印语句
(i1, i2) ->{System.out.println("合并:"+i1+"-"+i2);返回 i1+i2;});
你会看到这样的东西:
合并:0-0合并:0-0合并:1-0合并:1-0合并: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();
希望对您有所帮助!:)
I wrote this code to reduce a list of words to a long count of how many words start with an 'A'. I'm just writing it to learn Java 8, so I'd like to understand it a little better [Disclaimer: I realize this is probably not the best way to write this code; it's just for practice!].
Long countOfAWords = results.stream().reduce(
0L,
(a, b) -> b.charAt(0) == 'A' ? a + 1 : a,
Long::sum);
The middle parameter/lambda (called the accumulator) would seem to be capable of reducing the full list without the final 'Combiner' parameter. In fact, the Javadoc actually says:
The {@code accumulator} function acts as a fused mapper and accumulator, * which can sometimes be more efficient than separate mapping and reduction, * such as when knowing the previously reduced value allows you to avoid * some computation.
[Edit From Author] - The following statement is wrong, so don't let it confuse you; I'm just keeping it here so I don't ruin the original context of the answers.
Anyway, I can infer that the accumulator must just be outputting 1's and 0's which the combiner combines. I didn't find this particularly obvious from the documentation though.
My Question
Is there a way to see what the output would be before the combiner executes so I can see the list of 1's and 0's that the combiner combines? This would be helpful in debugging more complex situations which I'm sure I'll come across eventually.
解决方案The combiner does not reduce a list of 0's and 1's. When the stream is not run in parallel it's not used in this case so that the following loop is equivalent:
U result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
When you run the stream in parallel, the task is spanned into multiple threads. So for example the data in the pipeline is partitioned into chunks that evaluate and produce a result independently. Then the combiner is used to merge this results.
So you won't see a list that is reduced, but rather 2 values either the identity value or with another value computed by a task that are summed. For example if you add a print statement in the combiner
(i1, i2) -> {System.out.println("Merging: "+i1+"-"+i2); return i1+i2;});
you could see something like this:
Merging: 0-0
Merging: 0-0
Merging: 1-0
Merging: 1-0
Merging: 1-1
This would be helpful in debugging more complex situations which I'm sure I'll come across eventaully.
More generally if you want to see the data on the pipeline on the go you can use peek
(or the debugger could also help). So applied to your example:
long countOfAWords = result.stream().map(s -> s.charAt(0) == 'A' ? 1 : 0).peek(System.out::print).mapToLong(l -> l).sum();
which can output:
100100
[Disclaimer: I realize this is probably not the best way to write this code; it's just for practice!].
The idiomatic way to achieve your task would be to filter
the stream and then simply use count
:
long countOfAWords = result.stream().filter(s -> s.charAt(0) == 'A').count();
Hope it helps! :)
相关文章