Solución:
Esto puede ser suficiente en muchos casos.
stream.findAny().isPresent()
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:
static <T> Stream<T> throwIfEmpty(Stream<T> stream) {
Iterator<T> iterator = stream.iterator();
if (iterator.hasNext()) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
} else {
throw new NoSuchElementException("empty stream");
}
}
static <T> Stream<T> defaultIfEmpty(Stream<T> stream, Supplier<T> supplier) {
Iterator<T> iterator = stream.iterator();
if (iterator.hasNext()) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
} else {
return Stream.of(supplier.get());
}
}
Básicamente, convierte la corriente en un Iterator
para llamar hasNext()
en él, y si es cierto, encienda el Iterator
de vuelta a un Stream
. Esto es ineficiente porque todas las operaciones posteriores en la secuencia pasarán por el Iterator hasNext()
y next()
métodos, lo que también implica que la secuencia se procesa de manera efectiva secuencialmente (incluso si luego se vuelve 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 Spliterator
en lugar de un Iterator
. Esto potencialmente permite que el flujo devuelto tenga las mismas características que el flujo de entrada, incluida la ejecución en paralelo.
Si puede vivir con capacidades paralelas limitadas, la siguiente solución funcionará:
private static <T> Stream<T> nonEmptyStream(
Stream<T> stream, Supplier<RuntimeException> e) {
Spliterator<T> it=stream.spliterator();
return StreamSupport.stream(new Spliterator<T>() {
boolean seen;
public boolean tryAdvance(Consumer<? super T> action) {
boolean r=it.tryAdvance(action);
if(!seen && !r) throw e.get();
seen=true;
return r;
}
public Spliterator<T> trySplit() { return null; }
public long estimateSize() { return it.estimateSize(); }
public int characteristics() { return it.characteristics(); }
}, false);
}
Aquí hay un código de ejemplo que lo usa:
List<String> l=Arrays.asList("hello", "world");
nonEmptyStream(l.stream(), ()->new RuntimeException("No strings available"))
.forEach(System.out::println);
nonEmptyStream(l.stream().filter(s->s.startsWith("x")),
()->new RuntimeException("No strings available"))
.forEach(System.out::println);
El problema con la ejecución paralela (eficiente) es que el apoyo a la división del 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. Entonces el último de los fragmentos ejecutando tryAdvance
tiene que darse cuenta de que es el último (y tampoco pudo avanzar) en lanzar la excepción apropiada. Así que no agregué soporte para dividir aquí.