[java] 술어에 의해 스트림 제한

(잠재적으로 무한)을 제한하는 Java 8 스트림 작업이 있습니까? Stream첫 번째 요소가 술어와 일치하지 않을 때까지 있습니까?

Java 9에서는 takeWhile아래 예와 같이 10 미만의 모든 숫자를 인쇄 할 수 있습니다 .

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

Java 8에는 그러한 작업이 없으므로 일반적인 방법으로 구현하는 가장 좋은 방법은 무엇입니까?



답변

이러한 작업 은 Java 8 로 가능 해야 Stream하지만 효율적으로 수행 할 수있는 것은 아닙니다. 예를 들어, 요소를 순서대로 살펴보아야하므로 이러한 작업을 병렬화 할 필요는 없습니다.

API는이를 수행하는 쉬운 방법을 제공하지 않지만 아마도 가장 간단한 방법은을 수행 하고 “취약”구현을 갖도록 Stream.iterator()랩한 다음 a Iterator로 이동 Spliterator하는 것 Stream입니다. 또는 아마도이 Spliterator구현에서 더 이상 나눌 수는 없지만를 감싸십시오 .

다음의 검증되지 않은 구현의 takeWhileA의는 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);
}


답변

운영 takeWhiledropWhileJDK 9. 예제 코드에 추가되었습니다

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

JDK 9에서 컴파일하고 실행할 때 예상대로 정확하게 작동합니다.

JDK 9가 릴리스되었습니다. http://jdk.java.net/9/ 에서 다운로드 할 수 있습니다.


답변

allMatch()는 단락 기능이므로 처리를 중지하는 데 사용할 수 있습니다. 가장 큰 단점은 테스트를 두 번 수행해야한다는 것입니다. 한 번 처리해야하는지 확인하고 계속 진행해야하는지 확인해야합니다.

IntStream
    .iterate(1, n -> n + 1)
    .peek(n->{if (n<10) System.out.println(n);})
    .allMatch(n->n < 10);


답변

@StuartMarks에 대한 후속 조치로 . 내 StreamEx 라이브러리에는 takeWhile현재 JDK-9 구현과 호환되는 작업이 있습니다. JDK-9에서 실행될 때 JDK 구현에 위임 할 것 MethodHandle.invokeExact입니다 (실제로 빠릅니다). JDK-8에서 실행될 때는 “polyfill”구현이 사용됩니다. 따라서 내 라이브러리를 사용하면 다음과 같이 문제를 해결할 수 있습니다.

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


답변

takeWhileprotonpack 라이브러리가 제공하는 기능 중 하나입니다 .

Stream<Integer> infiniteInts = Stream.iterate(0, i -> i + 1);
Stream<Integer> finiteInts = StreamUtils.takeWhile(infiniteInts, i -> i < 10);

assertThat(finiteInts.collect(Collectors.toList()),
           hasSize(10));


답변

업데이트 : Java 9에는 Stream이제 takeWhile 이 제공됩니다. 메소드 .

해킹이나 다른 솔루션이 필요하지 않습니다. 그냥 사용하십시오!


나는 이것이 크게 향상 될 수 있다고 확신한다 : (누군가 그것을 스레드로부터 안전하게 만들 수있다)

Stream<Integer> stream = Stream.iterate(0, n -> n + 1);

TakeWhile.stream(stream, n -> n < 10000)
         .forEach(n -> System.out.print((n == 0 ? "" + n : "," + n)));

확실한 해킹은 … 우아하지는 않지만 작동하지만 ~ : D

class TakeWhile<T> implements Iterator<T> {

    private final Iterator<T> iterator;
    private final Predicate<T> predicate;
    private volatile T next;
    private volatile boolean keepGoing = true;

    public TakeWhile(Stream<T> s, Predicate<T> p) {
        this.iterator = s.iterator();
        this.predicate = p;
    }

    @Override
    public boolean hasNext() {
        if (!keepGoing) {
            return false;
        }
        if (next != null) {
            return true;
        }
        if (iterator.hasNext()) {
            next = iterator.next();
            keepGoing = predicate.test(next);
            if (!keepGoing) {
                next = null;
            }
        }
        return next != null;
    }

    @Override
    public T next() {
        if (next == null) {
            if (!hasNext()) {
                throw new NoSuchElementException("Sorry. Nothing for you.");
            }
        }
        T temp = next;
        next = null;
        return temp;
    }

    public static <T> Stream<T> stream(Stream<T> s, Predicate<T> p) {
        TakeWhile tw = new TakeWhile(s, p);
        Spliterator split = Spliterators.spliterator(tw, Integer.MAX_VALUE, Spliterator.ORDERED);
        return StreamSupport.stream(split, false);
    }

}


답변

java8 + rxjava를 사용할 수 있습니다 .

import java.util.stream.IntStream;
import rx.Observable;


// Example 1)
IntStream intStream  = IntStream.iterate(1, n -> n + 1);
Observable.from(() -> intStream.iterator())
    .takeWhile(n ->
          {
                System.out.println(n);
                return n < 10;
          }
    ).subscribe() ;


// Example 2
IntStream intStream  = IntStream.iterate(1, n -> n + 1);
Observable.from(() -> intStream.iterator())
    .takeWhile(n -> n < 10)
    .forEach( n -> System.out.println(n));