【问题标题】:Stream works on sequential, but buggy in parallel modeStream 在顺序模式下工作,但在并行模式下有问题
【发布时间】:2015-03-02 20:10:07
【问题描述】:

目前我正在阅读Java 8 Lambdas: Pragmatic Functional Programming(非常有趣且写得很好的书,没有推荐)。

在第 6 章之后有一个练习:

代码将列表中的每个数字相乘,然后将 结果由 5. 顺序运行正常,但并行运行时有bug。

public static int multiplyThrough(List<Integer> linkedListOfNumbers) {
    return linkedListOfNumbers.parallelStream()
                       .reduce(5, (acc, x) -> x * acc);
}

以及解决方案:

public static int multiplyThrough(List<Integer> numbers) {
    return 5 * numbers.parallelStream()
                      .reduce(1, (acc, x) -> x * acc);
}

我不明白为什么第一种方法在并行模式下是错误的,并且书中没有解释。

请解释一下。

【问题讨论】:

标签: java lambda parallel-processing java-8 java-stream


【解决方案1】:

javadoc 说:

标识值必须是累加器函数的标识。这意味着对于所有 t,accumulator.apply(identity, t) 等于 t。累加器函数必须是关联函数。

在您的第一个示例中显然不是这种情况:

5 * x 不等于 x。

它在并行模式下没有给出相同结果的原因是,在这种模式下,流被分割成块,并且对每个块应用缩减,然后每个块的结果被缩减。所以你最终乘以 5 几次,而在顺序模式下,乘以 5 只发生一次。

【讨论】:

  • 所以标识应该只有0或1(0 * 0等于0和1 * 1等于1)?
  • 身份 * 任何数字 必须等于该数字。只有 1 适用,0 * 5 不是 5。
  • @user2138356 请注意,一般情况下,“身份值”完全取决于操作。对于乘法,这是一个;另外,这是零。其他操作也会有所不同。
  • 您可以使用reduce(acc).orElse(identity) 表单完全消除此要求。现在identity 不需要是acc 函数的标识值。
  • 我不评论 OP 的案例。显然,他最终只需要乘以 5。我的评论更笼统,您可以避免编写具有标识值的函数。
猜你喜欢
  • 1970-01-01
  • 2018-07-31
  • 2014-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多