17

Java 8 では、Stream に reduce メソッドがあります。

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

アキュムレータ演算子は、その引数のいずれかを変更できますか? すべての例は、コレクションの要素を変更するのではなく、コレクションを変更することについて話していますが、JavaDoc がアキュムレータは NonInterfering であるべきだと言っているので、そうではないと思います。

したがって、具体的な例として、

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

しばらくの間、Integer変更可能sumで、2 番目のパラメーターの値を (その場で) 追加することによって最初のパラメーターを変更できるとしますか?

そうではないと思いますが、この干渉が問題を引き起こす場所の例も知りたいです。

4

2 に答える 2

13

いいえ。アキュムレータはその引数を変更すべきではありません。2 つの値を取り、新しい値を生成します。蓄積の過程で突然変異を使用したい場合 (例えば、文字列を連結する代わりに StringBuffer に蓄積する場合)、Stream.collect()このために設計された を使用します。

これを試みると間違った答えを生成するコードの例を次に示します。仮定の MutableInteger クラスで足し算をしたいとしましょう:

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

これが間違った答えになる理由の 1 つは、計算を並列に分割すると、2 つの計算が同じ変更可能な開始値を共有することになります。ご了承ください:

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

そのため、ストリームを自由に分割し、部分和0 + a + bと を計算して0 + c + dから、結果を追加できます。しかし、それらが同じ ID 値を共有していて、一方の計算の結果としてその値が変更された場合、もう一方は間違った値で始まる可能性があります。

(実装が価値があると判断した場合、逐次計算でもこれを実行できることに注意してください。)

于 2014-05-26T12:58:27.060 に答える
0

これは構文的には許されていますが、設計パターンに反するものであり、悪い考えだと思います。

  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);
  }

正解です。すべての 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]]

于 2014-05-26T13:01:26.000 に答える