Java 8中的reduce累加器是否允许修改其参数?

Gra*_*oss 16 java reduce accumulator java-8 java-stream

在Java 8中,Stream有一个方法减少:

T reduce(T identity, BinaryOperator<T> accumulator);
Run Code Online (Sandbox Code Playgroud)

累加器运算符是否允许修改其任一参数?我认为不是因为JavaDoc说累加器应该是NonInterfering,尽管所有的例子都谈到修改集合,而不是修改集合的元素.

所以,举一个具体的例子,如果我们有

 integers.reduce(0, Integer::sum);
Run Code Online (Sandbox Code Playgroud)

并假设一个Integer可变的时刻,sum可以通过向其添加(就地)第二个参数的值来修改其第一个参数吗?

我认为不是,但我也想要一个这个干扰导致问题的例子.

Bri*_*etz 12

不.累加器不应修改其参数; 它需要两个值并产生一个新值.如果你想在累积过程中使用变异(例如,将字符串累积到StringBuffer而不是连接),请使用Stream.collect()为此设计的.

这是一个代码示例,如果您尝试这个,会产生错误的答案.假设您想要使用假设的MutableInteger类进行添加:

// Don't do this
MutableInteger result = stream.reduce(new MutableInteger(0), (a,b) -> a.add(b.get()));
Run Code Online (Sandbox Code Playgroud)

这得到错误答案的一个原因是,如果我们并行地破坏计算,现在两个计算共享相同的可变起始值.注意:

a + b + c + d
= 0 + a + b + 0 + c + d  // 0 denotes identity
= (0 + a + b) + (0 + c + d) // associativity
Run Code Online (Sandbox Code Playgroud)

所以我们可以自由地分割流,计算部分0 + a + b0 + c + d,然后添加结果.但是如果它们共享相同的标识值,并且该值由于其中一个计算而发生变化,则另一个可能以错误的值开始.

(进一步注意,如果它认为值得的话,即使对于顺序计算,也允许实现这样做.)

  • 仍然,这不是使用错误的借口,只是因为正确的事情更复杂......非干涉意味着:它不能干扰同一流管道中的任何其他计算.这包括源代码,还包括一些愚蠢的东西,比如一个lambda修改某个状态,另一个在同一个管道中依赖于该状态的答案.这并不意味着您无法更新结果容器. (2认同)
  • 也许`收集'并不是那么复杂.我想你可以做一些像stream.collect(MutableInteger :: new,MutableInteger :: add,MutableInteger :: add). (2认同)