java - videos - ¿Hay alguna forma de reutilizar un Stream?
videos de reciclaje (7)
Estoy aprendiendo las nuevas características de Java 8, y mientras experimentaba con streams (
java.util.stream.Stream
) y recopiladores, me di cuenta de que un stream no se puede usar dos veces.
¿Hay alguna forma de reutilizarlo?
Ahora que lo pienso, esta voluntad de "reutilizar" un flujo es solo la voluntad de llevar a cabo el resultado deseado con una buena operación en línea. Entonces, básicamente, de lo que estamos hablando aquí, ¿qué podemos hacer para seguir procesando después de escribir una operación de terminal?
1) si la operación de su terminal devuelve una colección , el problema se resuelve de inmediato, ya que cada colección se puede volver a convertir en una secuencia (JDK 8).
List<Integer> l=Arrays.asList(5,10,14);
l.stream()
.filter(nth-> nth>5)
.collect(Collectors.toList())
.stream()
.filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));
2) si las operaciones de su terminal devuelven un opcional , con las mejoras de JDK 9 a la clase Opcional, puede convertir el resultado Opcional en una secuencia y obtener la agradable operación en línea deseada:
List<Integer> l=Arrays.asList(5,10,14);
l.stream()
.filter(nth-> nth>5)
.findAny()
.stream()
.filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));
3) si su operación de terminal devuelve algo más , realmente dudo que deba considerar una transmisión para procesar dicho resultado:
List<Integer> l=Arrays.asList(5,10,14);
boolean allEven=l.stream()
.filter(nth-> nth>5)
.allMatch(nth-> nth%2==0);
if(allEven){
...
}
Como otros han dicho, "no, no puedes".
Pero es útil recordar el práctico
summaryStatistics()
para muchas operaciones básicas:
Entonces en lugar de:
List<Person> personList = getPersons();
personList.stream().mapToInt(p -> p.getAge()).average().getAsDouble();
personList.stream().mapToInt(p -> p.getAge()).min().getAsInt();
personList.stream().mapToInt(p -> p.getAge()).max().getAsInt();
Usted puede:
// Can also be DoubleSummaryStatistics from mapToDouble()
IntSummaryStatistics stats = personList.stream()
.mapToInt(p-> p.getAge())
.summaryStatistics();
stats.getAverage();
stats.getMin();
stats.getMax();
Como otros han notado, el objeto de flujo en sí no se puede reutilizar.
Pero una forma de obtener el efecto de reutilizar una secuencia es extraer el código de creación de la secuencia a una función .
Puede hacerlo creando un método o un objeto de función que contenga el código de creación de flujo. Luego puede usarlo varias veces.
Ejemplo:
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// The normal way to use a stream:
List<String> result1 = list.stream()
.filter(i -> i % 2 == 1)
.map(i -> i * i)
.limit(10)
.map(i -> "i :" + i)
.collect(toList());
// The stream operation can be extracted to a local function to
// be reused on multiple sources:
Function<List<Integer>, List<String>> listOperation = l -> l.stream()
.filter(i -> i % 2 == 1)
.map(i -> i * i)
.limit(10)
.map(i -> "i :" + i)
.collect(toList());
List<String> result2 = listOperation.apply(list);
List<String> result3 = listOperation.apply(Arrays.asList(1, 2, 3));
// Or the stream operation can be extracted to a static method,
// if it doesn''t refer to any local variables:
List<String> result4 = streamMethod(list);
// The stream operation can also have Stream as argument and return value,
// so that it can be used as a component of a longer stream pipeline:
Function<Stream<Integer>, Stream<String>> streamOperation = s -> s
.filter(i -> i % 2 == 1)
.map(i -> i * i)
.limit(10)
.map(i -> "i :" + i);
List<String> result5 = streamOperation.apply(list.stream().map(i -> i * 2))
.filter(s -> s.length() < 7)
.sorted()
.collect(toCollection(LinkedList::new));
}
public static List<String> streamMethod(List<Integer> l) {
return l.stream()
.filter(i -> i % 2 == 1)
.map(i -> i * i)
.limit(10)
.map(i -> "i :" + i)
.collect(toList());
}
Si, por otro lado, ya tiene un objeto de secuencia que desea iterar varias veces, debe guardar el contenido de la secuencia en algún objeto de colección.
Luego puede obtener múltiples transmisiones con el mismo contenido de la colección.
Ejemplo:
public void test(Stream<Integer> stream) {
// Create a copy of the stream elements
List<Integer> streamCopy = stream.collect(toList());
// Use the copy to get multiple streams
List<Integer> result1 = streamCopy.stream() ...
List<Integer> result2 = streamCopy.stream() ...
}
De la documentation :
Se debe operar una secuencia (invocando una operación de secuencia intermedia o terminal) solo una vez.
Una implementación de flujo puede arrojar IllegalStateException si detecta que el flujo se está reutilizando.
Entonces la respuesta es no, las transmisiones no están destinadas a ser reutilizadas.
La
biblioteca funcional de Java
proporciona sus propios flujos que hacen lo que está pidiendo, es decir, son memorables y perezosos.
Puede usar sus métodos de conversión para convertir entre objetos SDK de Java y objetos FJ, por ejemplo,
Java8.JavaStream_Stream(stream)
devolverá un flujo FJ reutilizable dado un flujo JDK 8.
La idea general de Stream es que es única. Esto le permite crear fuentes no reentrenables (por ejemplo, leer las líneas desde la conexión de red) sin almacenamiento intermedio. Sin embargo, si desea reutilizar el contenido de Stream, puede volcarlo en la colección intermedia para obtener la "copia impresa":
Stream<MyType> stream = // get the stream from somewhere
List<MyType> list = stream.collect(Collectors.toList()); // materialize the stream contents
list.stream().doSomething // create a new stream from the list
list.stream().doSomethingElse // create one more stream from the list
Si no desea materializar la secuencia, en algunos casos hay formas de hacer varias cosas con la misma secuencia a la vez. Por ejemplo, puede consultar this o this pregunta para más detalles.
Si desea tener el efecto de reutilizar una secuencia, puede ajustar la expresión de la secuencia en un Proveedor y llamar a
myStreamSupplier.get()
cuando desee una nueva.
Por ejemplo:
Supplier<Stream<String>> sup = () -> someList.stream();
List<String> nonEmptyStrings = sup.get().filter(s -> !s.isEmpty()).collect(Collectors.toList());
Set<String> uniqueStrings = sup.get().collect(Collectors.toSet());