[java] Java 8 스트림이 비어 있는지 확인하는 방법은 무엇입니까?

Stream비 터미널 작업으로 a가 비어 있는지 확인 하고 그렇지 않은 경우 예외를 throw하려면 어떻게해야합니까?

기본적으로 아래 코드와 동일한 것을 찾고 있지만 중간에 스트림을 구체화하지 않습니다. 특히, 터미널 작업에서 스트림이 실제로 사용되기 전에는 검사가 발생하지 않아야합니다.

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();
}



답변

제한된 병렬 용량으로 생활 할 수있는 경우 다음 솔루션이 작동합니다.

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);
}

다음은이를 사용하는 몇 가지 예제 코드입니다.

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);

(효율적인) 병렬 실행의 문제는 분할을 지원 Spliterator하려면 조각 중 하나가 스레드로부터 안전한 방식으로 값을 보았는지 여부를 알 수있는 스레드로부터 안전한 방법 이 필요하다는 것입니다. 그런 다음 실행중인 마지막 프래그먼트 tryAdvance는 적절한 예외를 발생시키기위한 마지막 프래그먼트 (또한 진행할 수 없음)임을 인식해야합니다. 그래서 여기에 분할 지원을 추가하지 않았습니다.


답변

다른 답변과 코멘트는 스트림의 내용을 조사하기 위해 정확합니다. 하나는 터미널 작업을 추가해야하므로 스트림을 “소비”해야합니다. 그러나이를 수행하고 스트림의 전체 내용을 버퍼링하지 않고도 결과를 스트림으로 되돌릴 수 있습니다. 다음은 몇 가지 예입니다.

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());
    }
}

기본적으로 스트림 Iterator을 호출하려면 스트림을로 바꾸고, hasNext()참이면 Iterator다시 Stream. 이는 스트림에 대한 모든 후속 작업이 Iterator hasNext()next()메서드를 통과한다는 점에서 비효율적이며 , 이는 스트림이 순차적으로 효과적으로 처리된다는 것을 의미합니다 (나중에 병렬로 전환 되더라도). 그러나 이렇게하면 모든 요소를 ​​버퍼링하지 않고도 스트림을 테스트 할 수 있습니다.

사용하여이 작업을 수행 할 수있는 방법 아마이 Spliterator대신의가 Iterator. 이렇게하면 반환 된 스트림이 병렬 실행을 포함하여 입력 스트림과 동일한 특성을 가질 수 있습니다.


답변

이것은 많은 경우에 충분할 수 있습니다.

stream.findAny().isPresent()


답변

필터를 적용하려면 스트림에서 터미널 작업을 수행해야합니다. 따라서 소비 할 때까지 비어 있을지 알 수 없습니다.

할 수있는 최선의 방법은 findAny()터미널 작업으로 스트림을 종료 하는 것입니다.이 작업은 요소를 찾으면 중지되지만 요소가 없으면 모든 입력 목록을 반복하여 찾아야합니다.

이것은 입력 목록에 많은 요소가 있고 처음 몇 개 중 하나가 필터를 통과하는 경우에만 도움이됩니다. 스트림이 비어 있지 않다는 것을 알기 전에 목록의 작은 하위 집합 만 소비되어야하기 때문입니다.

물론 출력 목록을 생성하려면 새 스트림을 만들어야합니다.


답변

부울을 매핑하기에 충분해야한다고 생각합니다.

코드에서 이것은 다음과 같습니다.

boolean isEmpty = anyCollection.stream()
    .filter(p -> someFilter(p)) // Add my filter
    .map(p -> Boolean.TRUE) // For each element after filter, map to a TRUE
    .findAny() // Get any TRUE
    .orElse(Boolean.FALSE); // If there is no match return false


답변

Stuart의 아이디어에 따라 다음과 같이 할 수 있습니다 Spliterator.

static <T> Stream<T> defaultIfEmpty(Stream<T> stream, Stream<T> defaultStream) {
    final Spliterator<T> spliterator = stream.spliterator();
    final AtomicReference<T> reference = new AtomicReference<>();
    if (spliterator.tryAdvance(reference::set)) {
        return Stream.concat(Stream.of(reference.get()), StreamSupport.stream(spliterator, stream.isParallel()));
    } else {
        return defaultStream;
    }
}

stream.spliterator()작업이 스트림을 종료 한 다음 필요에 따라 다시 빌드하므로 병렬 스트림에서 작동한다고 생각 합니다.

내 사용 사례에서는 기본값이 Stream아닌 기본값 이 필요했습니다 . 이것이 필요한 것이 아니라면 변경하기가 매우 쉽습니다.


답변

나는 단순히 다음을 사용합니다.

stream.count()>0