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

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

T reduce(T identity, BinaryOperator accumulator); 

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

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

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

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

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

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

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

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

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

 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 ,然后添加结果。 但是如果它们共享相同的标识值,并且该值由于其中一个计算而发生变化,则另一个可能以错误的值开始。

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

这在语法上是允许的,但我认为它违背了设计模式并且是一个坏主意。

  static void accumulatorTest() { ArrayList points = new ArrayList<>(); points.add(new Point(5, 6)); points.add(new Point(0, 6)); points.add(new Point(1, 9)); points.add(new Point(4, 16)); BinaryOperator sumPoints = new BinaryOperator() { public Point apply(Point p1, Point p2) { p2.x += p1.x; p2.y += p1.y; return new Point(p2); //return p2 and the list is transformed into running total } }; Point sum = points.stream().reduce(new Point(0, 0), sumPoints); System.out.println(sum); System.out.println(points); } 

答案是对的; 我们得到所有x和y坐标的总和。 原始列表已修改,由输出确认:

java.awt.Point [x = 10,y = 37] [java.awt.Point [x = 5,y = 6],java.awt.Point [x = 5,y = 12],java.awt.Point [x = 6,y = 21],java.awt.Point [x = 10,y = 37]]

Interesting Posts