Is the accumulator of reduce in Java 8 allowed to modify its arguments?

前端 未结 2 729
萌比男神i
萌比男神i 2020-12-14 18:49

In Java 8, Stream has a method reduce:

T reduce(T identity, BinaryOperator accumulator);

Is the accumulator operator allowed to mo

相关标签:
2条回答
  • 2020-12-14 19:26

    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.

    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
    

    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.)

    0 讨论(0)
  • 2020-12-14 19:33

    This is allowed syntactically, but I think it runs against the design pattern and is a bad idea.

      static void accumulatorTest() {
         ArrayList<Point> 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<Point> sumPoints = new BinaryOperator<Point>() {
            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);
      }
    

    The answer is correct; we get the sum of all of the x and y coordinates. The original list is modified, confirmed by the output:

    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]]

    0 讨论(0)
提交回复
热议问题