【问题标题】:Java8 stream.reduce() with 3 parameters - getting transparency具有 3 个参数的 Java8 stream.reduce() - 获得透明度
【发布时间】:2016-02-28 05:24:44
【问题描述】:

我编写这段代码是为了将单词列表减少为有多少单词以“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} 函数充当一个融合映射器和 累加器, * 有时比单独的映射和归约更有效, *例如当知道先前减少的值时,您可以避免 * 一些计算。

[Edit From Author] - 下面的说法是错误的,所以不要让它混淆你;我只是将其保留在这里,以免破坏答案的原始上下文。

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

我的问题

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

【问题讨论】:

  • 为什么不简单地long countOfAWords=results.stream().filter(x->x.charAt(0)=='A').count();
  • 我当然可以,但我只是在玩弄新的体验机制;这不是真正的代码 :)

标签: java java-8 reduce java-stream accumulate


【解决方案1】:

查看发生了什么的一种方法是将方法引用Long::sum 替换为包含println 的lambda。

List<String> 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 值被组合在一起。

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

【讨论】:

  • 也感谢您的回答;在我的情况下,这绝对很容易使用。
【解决方案2】:

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

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

当您并行运行流时,任务会跨入多个线程。例如,管道中的数据被划分为独立评估和产生结果的块。然后使用组合器来合并这个结果。

因此,您不会看到减少的列表,而是 2 个值,要么是标识值,要么是由任务计算的另一个值相加。例如,如果您在组合器中添加打印语句

(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();

希望对您有所帮助! :)

【讨论】:

  • 这是我遇到过的最好的答案之一 :) 谢谢。
  • @JohnHumphreys-w00te 哇非常感谢你 :-) 很高兴它回答了你的问题!
猜你喜欢
  • 2013-02-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-05
  • 1970-01-01
  • 2015-06-26
  • 1970-01-01
相关资源
最近更新 更多