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.