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

2022-01-22 00:00:00 reduce java-8 java java-stream accumulator

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

In Java 8, Stream has a method reduce:

T reduce(T identity, BinaryOperator<T> accumulator);

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

Is the accumulator operator allowed to modify either of its arguments? I presume not since the JavaDoc says the accumulator should be NonInterfering, though all examples talk of modifying the collection, rather than modifying the elements of the collection.

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

So, for a concrete example, if we have

 integers.reduce(0, Integer::sum);

假设 Integer 是可变的,是否允许 sum 修改它的第一个参数,方法是(就地)添加第二个参数的值?

and suppose for a moment that Integer was mutable, would sum be allowed to modify its first parameter by adding to it (in place) the value of its second parameter?

我想不会,但我也想举个例子说明这种干扰导致问题的地方.

I presume not, but I would also like an example of where this Interfering causes a problem.

推荐答案

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

No. The accumulator should not modify its arguments; it takes two values and produces a new value. If you want to use mutation in the course of accumulation (e.g., accumulating strings into a StringBuffer instead of concatenating), use Stream.collect(), which is designed for this.

这是一个代码示例,如果您尝试这样做,则会产生错误的答案.假设你想用一个假设的 MutableInteger 类做加法:

Here's an example of code that produces the wrong answer if you try this. Let's say you want to do addition with a hypothetical MutableInteger class:

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

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

One reason this gets the wrong answer is that if we break the computation up in parallel, now two computations are sharing the same mutable starting value. Note that:

a + b + c + d
= 0 + a + b + 0 + c + d  // 0 denotes identity
= (0 + a + b) + (0 + c + d) // associativity

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

so we are free to split the stream, compute the partial sums 0 + a + b and 0 + c + d, and then add the results. But if they are sharing the same identity value, and that value is mutated as a result of one of the computations, the other may start with the wrong value.

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

(Note further that the implementation would be allowed to do this even for sequential computations, if it thought that was worthwhile.)

相关文章