Dada una corriente como { 0, 1, 2, 3, 4 }
,
¿Cómo puedo transformarlo de la manera más elegante en una forma dada?
{ new Pair(0, 1), new Pair(1, 2), new Pair(2, 3), new Pair(3, 4) }
(asumiendo, por supuesto, que he definido la clase Par)?
Editar: No se trata estrictamente de ints o streams primitivos. La respuesta debe ser general para una secuencia de cualquier tipo.
java
java-8
java-stream
Aleksandr Dubinsky
fuente
fuente
list.stream().map(i -> new Pair(i, i+1));
Map.Entry
una clase de par. (De acuerdo, algunos podrían considerar que es un truco, pero usar una clase incorporada es útil).Respuestas:
Mi biblioteca StreamEx , que amplía los flujos estándar, proporciona un
pairMap
método para todos los tipos de flujos. En el caso de los flujos primitivos, no cambia el tipo de flujo, pero se puede utilizar para realizar algunos cálculos. El uso más común es calcular diferencias:Para el flujo de objetos, puede crear cualquier otro tipo de objeto. Mi biblioteca no proporciona nuevas estructuras de datos visibles para el usuario como
Pair
(esa es la parte del concepto de biblioteca). Sin embargo, si tiene su propiaPair
clase y desea usarla, puede hacer lo siguiente:O si ya tienes alguno
Stream
:Esta funcionalidad se implementa mediante un spliterator personalizado . Tiene una sobrecarga bastante baja y se puede paralelizar muy bien. Por supuesto, funciona con cualquier fuente de flujo, no solo con listas / matrices de acceso aleatorio como muchas otras soluciones. En muchas pruebas funciona realmente bien. Aquí hay un punto de referencia de JMH donde encontramos todos los valores de entrada que preceden a un valor mayor utilizando diferentes enfoques (consulte esta pregunta).
fuente
StreamEx
implementosIterable
! ¡Hurra!)Stream
en aStreamEx
?StreamEx.of(stream)
. Hay otros métodos estáticos convenientes para crear el flujoCollection
, matrizReader
, etc. Editó la respuesta.pairMap
ordenado en secuencias secuenciales? En realidad, me gustaría tener forPairsOrdered (), pero como no existe tal método, ¿puedo simularlo de alguna manera?stream.ordered().forPairs()
ostream().pairMap().forEachOrdered()
?pairMap
es la operación intermedia con función de mapeador sin estado no interferente, el orden no se especifica para ella de la misma manera que para simplemap
. ElforPairs
no está ordenado por especificación, pero las operaciones no ordenadas están ordenadas de facto para flujos secuenciales. Sería bueno si formulara su problema original como una pregunta separada de stackoverflow para proporcionar más contexto.La biblioteca de flujos de Java 8 está orientada principalmente a dividir flujos en trozos más pequeños para el procesamiento en paralelo, por lo que las etapas de canalización con estado son bastante limitadas y no se admiten acciones como obtener el índice del elemento de flujo actual y acceder a elementos de flujo adyacentes.
Una forma típica de resolver estos problemas, con algunas limitaciones, por supuesto, es conducir la secuencia por índices y confiar en que los valores se procesen en alguna estructura de datos de acceso aleatorio como una ArrayList de la que se pueden recuperar los elementos. Si los valores estuvieran dentro
arrayList
, se podrían generar los pares solicitados haciendo algo como esto:Por supuesto, la limitación es que la entrada no puede ser un flujo infinito. Sin embargo, esta canalización se puede ejecutar en paralelo.
fuente
arrayList
) es de hecho una colección, por lo que no la marqué como respuesta. (¡Pero felicidades por tu insignia de oro!)Esto no es elegante, es una solución pirata, pero funciona para flujos infinitos
Ahora puede limitar su transmisión a la duración que desee
PD : espero que haya una mejor solución, algo como clojure
(partition 2 1 stream)
fuente
parallelStream
documento: "Para preservar el comportamiento correcto, estos parámetros de comportamiento no deben interferir y, en la mayoría de los casos, deben ser apátridas"Implementé un contenedor de spliterator que toma todos los
n
elementosT
del spliterator original y produceList<T>
:Se puede utilizar el siguiente método para crear una secuencia consecutiva:
Uso de muestra:
fuente
List<E>
elementos. Cada lista contienen
elementos consecutivos del flujo original. Compruébelo usted mismo;)(partition size step)
función y esta es la mejor manera de conseguirla.ArrayDeque
para el rendimiento, en lugar deLinkedList
.Puede hacer esto con el método Stream.reduce () (no he visto ninguna otra respuesta usando esta técnica).
fuente
Puede hacer esto en cyclops-react (contribuyo a esta biblioteca), usando el operador deslizante.
O
Suponiendo que el constructor de pares puede aceptar una colección con 2 elementos.
Si desea agrupar por 4 e incrementar por 2 eso también es compatible.
Los métodos estáticos equivalentes para crear una vista deslizante sobre java.util.stream.Stream también se proporcionan en la clase cyclops-streams StreamUtils .
Nota: - para la operación de un solo subproceso, ReactiveSeq sería más apropiado. LazyFutureStream extiende ReactiveSeq pero está principalmente orientado al uso concurrente / paralelo (es un Stream of Futures).
LazyFutureStream extiende ReactiveSeq, que extiende Seq desde el impresionante jOOλ (que extiende java.util.stream.Stream), por lo que las soluciones que presenta Lukas también funcionarían con cualquier tipo de Stream. Para cualquier persona interesada, las principales diferencias entre los operadores de ventana / deslizamiento son el obvio equilibrio relativo de potencia / complejidad y la idoneidad para su uso con flujos infinitos (el deslizamiento no consume el flujo, sino amortiguadores a medida que fluye).
fuente
La biblioteca de paquetes de protones proporciona la funcionalidad de ventana. Dada una clase de par y una secuencia, puede hacerlo así:
Ahora la
pairs
secuencia contiene:fuente
st
dos veces. ¿Puede esta biblioteca resolver el problema usando una sola secuencia?windowed
ha agregado la funcionalidad! Ver la edición.Encontrar parejas sucesivas
Si está dispuesto a utilizar una biblioteca de terceros y no necesita paralelismo, entonces jOOλ ofrece funciones de ventana de estilo SQL de la siguiente manera
Flexible
La
lead()
función accede al siguiente valor en orden transversal desde la ventana.Encontrar sucesivos triples / cuádruples / n-tuplas
Una pregunta en los comentarios pedía una solución más general, donde no se deberían recopilar pares sino n-tuplas (o posiblemente listas). Por tanto, aquí hay un enfoque alternativo:
Produciendo una lista de listas
Sin el
filter(w -> w.count() == n)
, el resultado seríaDescargo de responsabilidad: trabajo para la empresa detrás de jOOλ
fuente
w.lead().lead()
?tuple(w.value(), w.lead(1), w.lead(2))
sería una opción. Actualicé mi respuesta con una solución más genérica paralength = n
.window()
no es una operación perezosa que recopila todo el flujo de entrada en una colección intermedia y luego crea un nuevo flujo a partir de él.Comparator
se usa para reordenar las ventanas), entonces sería posible una optimización como esta , y es probable que se implemente en el futuro.Streams.zip(..)
está disponible en Guayaba , para quienes dependen de ella.Ejemplo:
fuente
Podemos usar RxJava ( biblioteca de extensión reactiva muy poderosa )
fuente
Observable.zip(obs, obs.skip(1), pair->{...})
hasta ahora! No sabía queObservable.buffer
tenía una versión con un paso (y estoy acostumbrado alzip
truco de Python). +1La operación es esencialmente con estado, por lo que no es realmente lo que los flujos deben resolver; consulte la sección "Comportamientos sin estado" en el javadoc :
Una solución aquí es introducir el estado en su flujo a través de un contador externo, aunque solo funcionará con un flujo secuencial.
fuente
Stream
:! = "Lambdas".StreamEx
biblioteca también es un buen hallazgo y podría ser una respuesta en sí misma. Mi comentario sobre "streams! = Lambdas" se refiere a que usted dice "La operación es esencialmente con estado, así que no es realmente lo que las lambdas deben resolver". Creo que querías usar la palabra "arroyos".En su caso, escribiría mi IntFunction personalizada que realiza un seguimiento del último int pasado y lo usaría para mapear el IntStream original.
fuente
Para el cálculo de las diferencias sucesivas en el tiempo (x-valores) de una serie de tiempo, utilizo el
stream
'scollect(...)
método:Donde el DifferenceCollector es algo como esto:
Probablemente podría modificar esto para adaptarlo a sus necesidades.
fuente
Finalmente descubrí una forma de engañar a Stream.reduce para poder manejar cuidadosamente los pares de valores; Hay una multitud de casos de uso que requieren esta función que no aparece de forma natural en JDK 8:
El truco que utilizo es el derecho de devolución; declaración.
fuente
reduce
ofrezca suficientes garantías para que esto funcione.Una solución elegante sería utilizar zip . Algo como:
Esto es bastante conciso y elegante, sin embargo, utiliza una lista como entrada. Una fuente de flujo infinito no se puede procesar de esta manera.
Otro problema (mucho más problemático) es que el zip junto con toda la clase Streams se ha eliminado recientemente de la API. El código anterior solo funciona con versiones b95 o anteriores. Entonces, con el último JDK, diría que no hay una solución elegante de estilo FP y ahora mismo podemos esperar que de alguna manera se reintroduzca zip en la API.
fuente
zip
fue eliminado. No recuerdo todo lo que había en laStreams
clase, pero algunas cosas se han migrado para ser métodos estáticos en laStream
interfaz, y también hay clasesStreamSupport
yStream.Builder
.zip
? Cualquiera sea la razón pedante que se pueda inventar, no justifica matarzip
.Este es un problema interesante. ¿Es bueno mi intento de híbrido a continuación?
Creo que no se presta al procesamiento en paralelo y, por lo tanto, puede ser descalificado.
fuente
Stream
, no unList
. Por supuesto, también podemos sacar un iterador de una secuencia, por lo que esta podría ser una solución válida. Sin embargo, es un enfoque original.Como han observado otros, debido a la naturaleza del problema, se requiere cierta condición de estado.
Me enfrenté a un problema similar, en el que quería lo que era esencialmente la función LEAD de Oracle SQL. Mi intento de implementar eso está a continuación.
fuente
Puede lograrlo utilizando una cola limitada para almacenar elementos que fluyen a través de la secuencia (que se basa en la idea que describí en detalle aquí: ¿Es posible obtener el siguiente elemento en la secuencia? )
El siguiente ejemplo define primero la instancia de la clase BoundedQueue que almacenará los elementos que pasan por la secuencia (si no le gusta la idea de extender la LinkedList, consulte el enlace mencionado anteriormente para obtener un enfoque alternativo y más genérico). Más tarde, simplemente combine dos elementos posteriores en una instancia de Pair:
fuente
Estoy de acuerdo con @aepurniet pero en su lugar map tienes que usar mapToObj
fuente
Ejecute un
for
bucle que va desde 0 hastalength-1
de su flujofuente