Limitar una secuencia por un predicado

Resuelto MForster asked hace 10 años • 19 respuestas

¿Existe una operación de flujo de Java 8 que limite a (potencialmente infinito) Streamhasta que el primer elemento no coincida con un predicado?

En Java 9 podemos usar takeWhilecomo en el siguiente ejemplo para imprimir todos los números menores de 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 implementarla de manera general?

MForster avatar Dec 23 '13 22:12 MForster
Aceptado

Operaciones takeWhiley dropWhilese han agregado a JDK 9. Su código de ejemplo

IntStream
    .iterate(1, n -> n + 1)
    .takeWhile(n -> n < 10)
    .forEach(System.out::println);

se comportará exactamente como espera cuando se compila y ejecuta en JDK 9.

JDK 9 ha sido lanzado. Está disponible para descargar aquí: Lanzamientos de JDK 9 .

Stuart Marks avatar Aug 31 '2015 05:08 Stuart Marks

Tal operación debería ser posible con 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 observar los elementos en orden.

La API no proporciona una manera fácil de hacerlo, pero probablemente la forma más sencilla es tomar Stream.iterator(), empaquetar el Iteratorpara tener una implementación "durante un tiempo" y luego volver a a Spliteratory luego a Stream. O, tal vez, envolver el archivo Spliterator, aunque ya no se puede dividir en esta implementación.

Aquí hay una implementación no probada de takeWhileen Spliterator:

static <T> Spliterator<T> takeWhile(
    Spliterator<T> splitr, Predicate<? super T> predicate) {
  return new Spliterators.AbstractSpliterator<T>(splitr.estimateSize(), 0) {
    boolean stillGoing = true;
    @Override public boolean tryAdvance(Consumer<? super T> consumer) {
      if (stillGoing) {
        boolean hadNext = splitr.tryAdvance(elem -> {
          if (predicate.test(elem)) {
            consumer.accept(elem);
          } else {
            stillGoing = false;
          }
        });
        return hadNext && stillGoing;
      }
      return false;
    }
  };
}

static <T> Stream<T> takeWhile(Stream<T> stream, Predicate<? super T> predicate) {
   return StreamSupport.stream(takeWhile(stream.spliterator(), predicate), false);
}
Louis Wasserman avatar Dec 24 '2013 19:12 Louis Wasserman

allMatch()es una función de cortocircuito, por lo que puede usarla para detener el procesamiento. La principal desventaja es que tienes que hacer la prueba dos veces: una para ver si debes procesarla y otra para ver si debes continuar.

IntStream
    .iterate(1, n -> n + 1)
    .peek(n->{if (n<10) System.out.println(n);})
    .allMatch(n->n < 10);
Michael Rowley avatar Nov 13 '2014 22:11 Michael Rowley