method how array java java-8 reduce accumulator java-stream

how - java stream reduce methods



¿Se permite al acumulador de reduce en Java 8 modificar sus argumentos? (2)

Esto está permitido sintácticamente, pero creo que va en contra del patrón de diseño y es una mala 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); }

La respuesta es correcta; obtenemos la suma de todas las coordenadas x e y. La lista original se modifica, confirmada por la salida:

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

En Java 8, Stream tiene un método de reducción:

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

¿Se le permite al operador del acumulador modificar alguno de sus argumentos? Supongo que no, ya que el JavaDoc dice que el acumulador debería ser No Interferente, aunque todos los ejemplos hablan de modificar la colección, en lugar de modificar los elementos de la colección.

Entonces, para un ejemplo concreto, si tenemos

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

y supongamos por un momento que Integer era mutable, ¿se le permitiría a la sum modificar su primer parámetro agregándole (en su lugar) el valor de su segundo parámetro?

Supongo que no, pero también me gustaría un ejemplo de dónde esta Interferencia causa un problema.


No. El acumulador no debe modificar sus argumentos; toma dos valores y produce un nuevo valor. Si desea usar la mutación en el curso de la acumulación (por ejemplo, acumular cadenas en un StringBuffer en lugar de concatenar), use Stream.collect() , que está diseñado para esto.

Aquí hay un ejemplo de código que produce la respuesta incorrecta si intentas esto. Digamos que quieres hacer una adición con una clase hipotética de MutableInteger:

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

Una razón por la que la respuesta es incorrecta es que si dividimos el cálculo en paralelo, ahora dos cálculos comparten el mismo valor de inicio mutable. Tenga en cuenta que:

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

por lo tanto, somos libres de dividir la secuencia, calcular las sumas parciales 0 + a + b y 0 + c + d , y luego agregar los resultados. Pero si comparten el mismo valor de identidad, y ese valor se muta como resultado de uno de los cálculos, el otro puede comenzar con el valor incorrecto.

(Tenga en cuenta además que se permitiría a la implementación hacer esto incluso para cálculos secuenciales, si considerara que valía la pena).