java java-8 reduce foldleft

Equivalente del pliegue de Scala en Java 8



java-8 reduce (4)

El método que está buscando es java.util.Stream.reduce , particularmente la sobrecarga con tres parámetros, identidad, acumulador y función binaria. Ese es el equivalente correcto a foldLeft de Scala.

Sin embargo, no se le permite usar Java para reduce esa manera, y tampoco es el foldLeft de foldLeft de Scala. Utilice collect lugar.

¿Cuál es el equivalente de la gran foldLeft de Scala en Java 8?

Tuve la tentación de pensar que se reduce , pero reducir tiene que devolver algo de tipo idéntico a lo que reduce.

Ejemplo:

import java.util.List; public class Foo { // this method works pretty well public int sum(List<Integer> numbers) { return numbers.stream() .reduce(0, (acc, n) -> (acc + n)); } // this method makes the file not compile public String concatenate(List<Character> chars) { return chars.stream() .reduce(new StringBuilder(""), (acc, c) -> acc.append(c)).toString(); } }

El problema en el código anterior es el acc umulator: new StringBuilder("")

Por lo tanto, ¿podría alguien indicarme el equivalente correcto de la foldLeft de foldLeft / corregir mi código?


No hay equivalente de foldLeft en la API Stream de Java 8. Como han dicho otros, reduce(identity, accumulator, combiner) se acerca, pero no es equivalente a foldLeft porque requiere que el tipo B resultante se combine con sí mismo y sea asociativo (en otros términos, sea como monoide), una propiedad que no cada tipo tiene.

También hay una solicitud de mejora para esto: agregar la operación del terminal Stream.foldLeft ()

Para ver por qué reducir no funciona, considere el siguiente código, donde pretende ejecutar una serie de operaciones aritméticas que comienzan con el número dado:

val arithOps = List((''+'', 1), (''*'', 4), (''-'', 2), (''/'', 5)) val fun: (Int, (Char, Int)) => Int = { case (x, (''+'', y)) => x + y case (x, (''-'', y)) => x - y case (x, (''*'', y)) => x * y case (x, (''/'', y)) => x / y } val number = 2 arithOps.foldLeft(number)(fun) // ((2 + 1) * 4 - 2) / 5

Si intentaste escribir reduce(2, fun, combine) , ¿qué función combinadora podrías pasar que combina dos números? Sumar los dos números juntos claramente no lo resuelve. Además, el valor 2 claramente no es un elemento de identidad .

Tenga en cuenta que ninguna operación que requiera una ejecución secuencial se puede expresar en términos de reduce . foldLeft es en realidad más genérico que reduce : puede implementar reduce con foldLeft pero no puede implementar foldLeft con reduce .


Otros son correctos, sin embargo no hay equivalente. Aquí hay una utilidad que viene cerca

<U, T> U foldLeft(Collection<T> sequence, U identity, BiFunction<U, ? super T, U> accumulator) { U result = identity; for (T element : sequence) result = accumulator.apply(result, element); return result; }

su caso utilizando el método anterior se vería como

public String concatenate(List<Character> chars) { return foldLeft(chars, new StringBuilder(""), StringBuilder::append).toString(); }

O sin el método lambda ref azúcar,

public String concatenate(List<Character> chars) { return foldLeft(chars, new StringBuilder(""), (stringBuilder, character) -> stringBuilder.append(character)).toString(); }


Actualizar:

Aquí está el intento inicial de arreglar su código:

public static String concatenate(List<Character> chars) { return chars .stream() .reduce(new StringBuilder(), StringBuilder::append, StringBuilder::append).toString(); }

Utiliza el siguiente método de reducción :

<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);

Puede parecer confuso, pero si miras a los javadocs, hay una buena explicación que puede ayudarte a comprender rápidamente los detalles. La reducción es equivalente al siguiente código:

U result = identity; for (T element : this stream) result = accumulator.apply(result, element) return result;

Para una explicación más detallada por favor verifique esta fuente .

Sin embargo, este uso no es correcto porque viola el contrato de reducción que establece que el acumulador debe ser una función sin estado, sin interferencia, asociativa para incorporar un elemento adicional en un resultado . En otras palabras, dado que la identidad es mutable, el resultado se romperá en caso de ejecución paralela.

Como se señala en los comentarios a continuación, una opción correcta es utilizar la reducción de la siguiente manera:

return chars.stream().collect( StringBuilder::new, StringBuilder::append, StringBuilder::append).toString();

El proveedor StringBuilder::new se utilizará para crear contenedores reutilizables que luego se combinarán.