¿Cómo puedo verificar si a Stream
está vacío y lanzar una excepción si no lo está, como una operación no terminal?
Básicamente, estoy buscando algo equivalente al código a continuación, pero sin materializar el flujo intermedio. En particular, la verificación no debe ocurrir antes de que la secuencia sea consumida realmente por una operación de terminal.
public Stream<Thing> getFilteredThings() {
Stream<Thing> stream = getThings().stream()
.filter(Thing::isFoo)
.filter(Thing::isBar);
return nonEmptyStream(stream, () -> {
throw new RuntimeException("No foo bar things available")
});
}
private static <T> Stream<T> nonEmptyStream(Stream<T> stream, Supplier<T> defaultValue) {
List<T> list = stream.collect(Collectors.toList());
if (list.isEmpty()) list.add(defaultValue.get());
return list.stream();
}
java
java-8
java-stream
Cefalópodo
fuente
fuente
Respuestas:
Si puede vivir con capacidades paralelas limitadas, la siguiente solución funcionará:
Aquí hay un código de ejemplo que lo usa:
El problema con la ejecución en paralelo (eficiente) es que admitir la división de la
Spliterator
requiere una forma segura de subprocesos para notar si alguno de los fragmentos ha visto algún valor de una manera segura para subprocesos. Luego, el último de los fragmentos en ejecucióntryAdvance
debe darse cuenta de que es el último (y tampoco pudo avanzar) en lanzar la excepción apropiada. Entonces no agregué soporte para dividir aquí.fuente
Las otras respuestas y comentarios son correctos en el sentido de que para examinar el contenido de un flujo, se debe agregar una operación de terminal, "consumiendo" así el flujo. Sin embargo, se puede hacer esto y convertir el resultado de nuevo en una secuencia, sin almacenar en búfer todo el contenido de la secuencia. Aqui hay un par de ejemplos:
Básicamente, convierta la secuencia en un
Iterator
para invocarlahasNext()
y, si es cierto, convierta la parteIterator
posterior en unStream
. Esto es ineficiente porque todas las operaciones posteriores en la secuencia pasarán por el IteradorhasNext()
ynext()
métodos , lo que también implica que la secuencia se procesa de manera efectiva de forma secuencial (incluso si luego se convierte en paralelo). Sin embargo, esto le permite probar la transmisión sin almacenar en búfer todos sus elementos.Probablemente haya una forma de hacer esto usando un en
Spliterator
lugar de unIterator
. Esto potencialmente permite que el flujo devuelto tenga las mismas características que el flujo de entrada, incluida la ejecución en paralelo.fuente
estimatedSize
echaracteristics
incluso podría mejorar el rendimiento de un solo subproceso. Simplemente sucedió que escribí laSpliterator
solución mientras usted publicaba laIterator
solución ...tryAdvance
antes que elStream
hace convierte la naturaleza perezosa delStream
en una corriente "parcialmente perezosa". También implica que buscar el primer elemento ya no es una operación en paralelo, ya que tienes que dividir primero y hacerlotryAdvance
en las partes divididas al mismo tiempo para hacer una operación en paralelo real, según tengo entendido. Si la única operación del terminal esfindAny
o similar, destruiría toda laparallel()
solicitud.tryAdvance
antes de que lo haga la transmisión y debe envolver cada parte dividida en un proxy y recopilar la información "hasAny" de todas las operaciones simultáneas por su cuenta y asegurarse de que la última operación simultánea arroje la excepción deseada si el el arroyo estaba vacío. Un montón de cosas ...Esto puede ser suficiente en muchos casos.
fuente
Debe realizar una operación de terminal en la secuencia para que se aplique cualquiera de los filtros. Por lo tanto, no puede saber si estará vacío hasta que lo consuma.
Lo mejor que puede hacer es finalizar la transmisión con un
findAny()
operación de terminal, que se detendrá cuando encuentre cualquier elemento, pero si no hay ninguno, tendrá que iterar sobre toda la lista de entrada para averiguarlo.Esto solo lo ayudaría si la lista de entrada tiene muchos elementos, y uno de los primeros pasa los filtros, ya que solo un pequeño subconjunto de la lista tendría que consumirse antes de saber que Stream no está vacío.
Por supuesto, aún tendrá que crear una nueva secuencia para producir la lista de salida.
fuente
anyMatch(alwaysTrue())
, creo que es el más cercano ahasAny
.anyMatch(alwaysTrue())
coincide perfectamente con la semántica prevista de suhasAny
, dándole unaboolean
lugar deOptional<T>
--- pero estamos dividiendo los pelos aquí :)alwaysTrue
es un predicado de Guayaba.anyMatch(e -> true)
luego.Creo que debería ser suficiente para mapear un booleano
En código esto es:
fuente
Stream.anyMatch()
Siguiendo la idea de Stuart, esto podría hacerse con algo
Spliterator
así:Creo que esto funciona con
stream.spliterator()
transmisiones paralelas ya que la operación terminará la transmisión y luego la reconstruirá según sea necesarioEn mi caso de uso, necesitaba un valor predeterminado en
Stream
lugar de uno predeterminado. eso es bastante fácil de cambiar si esto no es lo que necesitafuente
Spliterator
Me pregunto cómo se comparan los dos.