¿Existe una operación de flujo Java 8 que limite un (potencialmente infinito) Stream
hasta que el primer elemento no coincida con un predicado?
En Java 9 podemos usar takeWhile
como en el ejemplo a continuación para imprimir todos los números menores que 10.
IntStream
.iterate(1, n -> n + 1)
.takeWhile(n -> n < 10)
.forEach(System.out::println);
Como no existe tal operación en Java 8, ¿cuál es la mejor manera de implementarlo de manera general?
java
java-8
java-stream
MForster
fuente
fuente
IntStream.iterate(1, n->n<10, n->n+1).forEach(System.out::print);
Respuestas:
Tal operación debería ser posible con un Java 8
Stream
, pero no necesariamente se puede hacer de manera eficiente; por ejemplo, no necesariamente se puede paralelizar dicha operación, ya que hay que mirar los elementos en orden.La API no proporciona una manera fácil de hacerlo, pero lo que probablemente sea la forma más sencilla es tomar
Stream.iterator()
, envolverIterator
para tener una implementación de "toma de tiempo", y luego volver a aySpliterator
luego aStream
. O, tal vez, envolverloSpliterator
, aunque en realidad ya no se puede dividir en esta implementación.Aquí hay una implementación no probada de
takeWhile
en unSpliterator
:fuente
Operaciones
takeWhile
ydropWhile
se han agregado a JDK 9. Su código de ejemplose comportará exactamente como lo espera cuando se compila y ejecuta bajo JDK 9.
JDK 9 ha sido lanzado. Está disponible para descargar aquí: http://jdk.java.net/9/
fuente
takeWhile
/dropWhile
: download.java.net/jdk9/docs/api/java/util/stream/Stream.htmltakeWhile
y endropWhile
lugar delimitWhile
yskipWhile
, por coherencia con la API existente?takeWhile
ydropWhile
están bastante extendidos, ocurren en Scala, Python, Groovy, Ruby, Haskell y Clojure. La asimetría conskip
ylimit
es lamentable. Talskip
ylimit
debería haber sido llamadodrop
ytake
, pero esos no son tan intuitivos a menos que ya esté familiarizado con Haskell.dropXXX
ytakeXXX
son términos más populares, pero personalmente puedo vivir con más SQL-esquelimitXXX
yskipXXX
. Encuentro esta nueva asimetría mucho más confusa que la elección individual de términos ... :) (por cierto: Scala también tienedrop(int)
ytake(int)
)allMatch()
es una función de cortocircuito, por lo que puede usarla para detener el procesamiento. La principal desventaja es que debe hacer su prueba dos veces: una para ver si debe procesarla y otra vez para ver si debe continuar.fuente
Stream.allMatch()
es una operación de cortocircuito . Entonces esto se completará incluso en una secuencia infinita comoIntStream.iterate()
. Por supuesto, en retrospectiva, esta es una optimización sensata.peek
. Si lo encontraba el próximo mes, me tomaría un minuto preguntarme por qué el programador que tenía delante verificó siallMatch
ignoraba la respuesta.Como seguimiento a la respuesta de @StuartMarks . Mi biblioteca StreamEx tiene la
takeWhile
operación que es compatible con la implementación actual de JDK-9. Cuando se ejecuta bajo JDK-9, simplemente delegará a la implementación de JDK (a través de laMethodHandle.invokeExact
cual es realmente rápido). Cuando se ejecuta bajo JDK-8, se utilizará la implementación "polyfill". Entonces, usando mi biblioteca, el problema puede resolverse así:fuente
takeWhile
es una de las funciones proporcionadas por la biblioteca protonpack .fuente
Actualización: Java 9
Stream
ahora viene con un método takeWhile .No hay necesidad de hacks u otras soluciones. ¡Solo usa eso!
Estoy seguro de que esto se puede mejorar en gran medida: (alguien podría hacerlo seguro para subprocesos tal vez)
Un truco seguro ... No es elegante, pero funciona ~: D
fuente
Puede usar java8 + rxjava .
fuente
En realidad, hay 2 formas de hacerlo en Java 8 sin bibliotecas adicionales o utilizando Java 9.
Si desea imprimir números del 2 al 20 en la consola, puede hacer esto:
o
La salida es en ambos casos:
Nadie mencionó anyMatch todavía. Esta es la razón de esta publicación.
fuente
Esta es la fuente copiada de JDK 9 java.util.stream.Stream.takeWhile (Predicate). Una pequeña diferencia para trabajar con JDK 8.
fuente
Aquí hay una versión realizada en ints, como se hizo en la pregunta.
Uso:
Aquí hay código para StreamUtil:
fuente
Ir a la biblioteca AbacusUtil . Proporciona la API exacta que desea y más:
Declaración: Soy el desarrollador de AbacusUtil.
fuente
No puede abortar una transmisión excepto por una operación de terminal de cortocircuito, lo que dejaría algunos valores de transmisión sin procesar independientemente de su valor. Pero si solo desea evitar operaciones en una secuencia, puede agregar una transformación y filtro a la secuencia:
Eso transforma el flujo de cosas en nulos cuando las cosas cumplen alguna condición, luego filtra los nulos. Si está dispuesto a disfrutar de los efectos secundarios, puede establecer el valor de la condición en verdadero una vez que se encuentre algo, para que todas las cosas posteriores se filtren independientemente de su valor. Pero incluso si no, puede guardar una gran cantidad de procesamiento (si no todo) al filtrar los valores fuera de la secuencia que no desea procesar.
fuente
Incluso tenía un requisito similar: invocar el servicio web, si falla, vuelva a intentarlo 3 veces. Si falla incluso después de estas muchas pruebas, envíe una notificación por correo electrónico. Después de googlear mucho,
anyMatch()
vino como salvador. Mi código de muestra de la siguiente manera. En el siguiente ejemplo, si el método webServiceCall devuelve verdadero en la primera iteración en sí, stream no itera más como lo hemos llamadoanyMatch()
. Creo que esto es lo que estás buscando.fuente
Si conoce la cantidad exacta de repeticiones que se realizarán, puede hacer
fuente
en lugar del pico, puede usar mapToObj para devolver el objeto o mensaje final
fuente
Si tiene un problema diferente, puede ser necesaria una solución diferente, pero para su problema actual, simplemente iría con:
fuente
Podría estar un poco fuera de tema, pero esto es para lo que tenemos
List<T>
más que paraStream<T>
.Primero necesitas tener un
take
método util. Este método toma los primerosn
elementos:simplemente funciona como
scala.List.take
ahora será bastante simple escribir un
takeWhile
método basado entake
funciona así:
esta implementación itera la lista parcialmente por algunas veces pero no agregará
O(n^2)
operaciones de agregar . Espero que sea aceptable.fuente
Tengo otra solución rápida al implementar esto (que de hecho es muy inmundo, pero ya entiendes la idea):
fuente
current
nunca.equals(e)
, obtendrás un bucle sin fin. Ambos, incluso si posteriormente solicita, por ejemplo.limit(1)
. Eso es mucho peor que 'inmundo' .Aquí está mi intento de usar solo la biblioteca Java Stream.
fuente
filter
supone que el predicado no tiene estado.System.out.println
Es un efecto secundario.