Tengo un conjunto de datos representado por una secuencia Java 8:
Stream<T> stream = ...;
Puedo ver cómo filtrarlo para obtener un subconjunto aleatorio, por ejemplo
Random r = new Random();
PrimitiveIterator.OfInt coin = r.ints(0, 2).iterator();
Stream<T> heads = stream.filter((x) -> (coin.nextInt() == 0));
También puedo ver cómo podría reducir esta secuencia para obtener, por ejemplo, dos listas que representan dos mitades aleatorias del conjunto de datos, y luego convertirlas nuevamente en secuencias. Pero, ¿hay una forma directa de generar dos secuencias a partir de la inicial? Algo como
(heads, tails) = stream.[some kind of split based on filter]
Gracias por cualquier idea.
java
java-8
java-stream
usuario1148758
fuente
fuente
Stream
a múltiplesStream
s sin conversión intermedia , aunque creo que las personas que llegaron a esta pregunta en realidad están buscando la manera de lograrlo, independientemente de esa restricción, que es la respuesta de Mark. Esto puede deberse al hecho de que la pregunta en el título no es la misma que en la descripción .Respuestas:
No exactamente. No puedes obtener dos
Stream
s de uno; esto no tiene sentido: ¿cómo iteraría sobre uno sin necesidad de generar el otro al mismo tiempo? Una secuencia solo se puede operar una vez.Sin embargo, si desea volcarlos en una lista o algo, podría hacer
fuente
stream.collect(...)
para con seguridad predefinida para subprocesosCollectors
, que funciona bien incluso en colecciones no seguras para subprocesos (sin contención de bloqueo sincronizado). La mejor respuesta por @MarkJeronimus.Se puede usar un colector para esto.
Collectors.partitioningBy()
fábrica.Esto creará un
Map
desdeBoolean
hastaList
y colocará elementos en una u otra lista según unPredicate
.Nota: Dado que la transmisión debe consumirse completa, esto no puede funcionar en transmisiones infinitas. Y debido a que el flujo se consume de todos modos, este método simplemente los coloca en listas en lugar de crear un nuevo flujo con memoria. Siempre puede transmitir esas listas si necesita transmisiones como salida.
Además, no es necesario el iterador, ni siquiera en el ejemplo de solo cabezas que proporcionó.
Collectors.groupingBy()
fábrica.En caso de que los flujos no lo sean
Stream
, pero uno de los flujos primitivos parezcaIntStream
, entonces este.collect(Collectors)
método no está disponible. Tendrás que hacerlo de forma manual sin una fábrica de colectores. Su implementación se ve así:[Ejemplo 2.0 desde 2020-04-16]
En este ejemplo, inicializo las ArrayLists con el tamaño completo de la colección inicial (si esto se conoce). Esto evita eventos de cambio de tamaño incluso en el peor de los casos, pero potencialmente puede engullir 2 * N * T espacio (N = número inicial de elementos, T = número de subprocesos). Para cambiar el espacio por la velocidad, puede omitirlo o usar su conjetura mejor educada, como el mayor número esperado de elementos en una partición (generalmente un poco más de N / 2 para una división equilibrada).
Espero no ofender a nadie usando un método Java 9. Para la versión Java 8, mire el historial de edición.
fuente
stream.boxed().collect(...);
! Hará lo que se anuncia: convierte la primitivaIntStream
a laStream<Integer>
versión en caja .(map, x) -> { boolean partition = p.test(x); List<Integer> list = map.get(partition); list.add(x); }
usted, simplemente puede usar(map, x) -> map.get(p.test(x)).add(x)
. Además, no veo ninguna razón por la cual lacollect
operación no debería ser segura para subprocesos. Funciona exactamente como se supone que funciona y muy cerca de cómoCollectors.partitioningBy(p)
funcionaría. Pero usaría un enIntPredicate
lugar dePredicate<Integer>
cuando no lo useboxed()
, para evitar el boxeo dos veces.Me topé con esta pregunta y siento que una transmisión bifurcada tiene algunos casos de uso que podrían ser válidos. Escribí el siguiente código como consumidor para que no haga nada, pero podría aplicarlo a las funciones y cualquier otra cosa que pueda encontrar.
Ahora su implementación de código podría ser algo como esto:
fuente
Desafortunadamente, lo que pides está directamente mal visto en JavaDoc de Stream :
Puede evitar esto utilizando
peek
u otros métodos si realmente desea ese tipo de comportamiento. En este caso, lo que debe hacer es, en lugar de intentar hacer una copia de seguridad de dos secuencias de la misma fuente de secuencia original con un filtro de bifurcación, duplicaría su secuencia y filtraría cada uno de los duplicados adecuadamente.Sin embargo, es posible que desee reconsiderar si
Stream
es la estructura adecuada para su caso de uso.fuente
List<Stream> forkStream(Stream s)
pero mis secuencias resultantes estarán respaldadas al menos parcialmente por colecciones y no directamente por la secuencia subyacente, en lugar de decirfilter
cuál no es una operación de secuencia terminal.Esto va en contra del mecanismo general de Stream. Digamos que puede dividir Stream S0 a Sa y Sb como quisiera. Realizar cualquier operación de terminal, por ejemplo
count()
, en Sa necesariamente "consumirá" todos los elementos en S0. Por lo tanto, Sb perdió su fuente de datos.Anteriormente, Stream tenía un
tee()
método, creo, que duplicaba un flujo a dos. Se ha eliminado ahora.Sin embargo, Stream tiene un método peek (), es posible que pueda usarlo para cumplir con sus requisitos.
fuente
peek
es exactamente lo que solía sertee
.no exactamente, pero puede lograr lo que necesita invocando
Collectors.groupingBy()
. crea una nueva colección y luego puede instanciar transmisiones en esa nueva colección.fuente
Esta fue la respuesta menos mala que se me ocurrió.
Esto toma una secuencia de enteros y los divide en 5. Para los mayores de 5, filtra solo los números pares y los coloca en una lista. Por lo demás, los une con |.
salidas:
No es ideal, ya que recopila todo en colecciones intermedias que rompen la corriente (¡y tiene demasiados argumentos!)
fuente
Me topé con esta pregunta mientras buscaba una forma de filtrar ciertos elementos de una secuencia y registrarlos como errores. Por lo tanto, realmente no necesitaba dividir la secuencia, sino adjuntar una acción de terminación prematura a un predicado con sintaxis discreta. Esto es lo que se me ocurrió:
fuente
Versión más corta que usa Lombok
fuente
Qué tal si:
fuente