반복자를 스트림으로 변환하는 방법?


468

나는 변환하는 간결한 방법을 찾고 있는데요 IteratorA를 Stream"보기"스트림으로 반복자 또는 더 구체적으로.

성능상의 이유로 새 목록에서 반복자의 사본을 피하고 싶습니다.

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Collection<String> copyList = new ArrayList<String>();
sourceIterator.forEachRemaining(copyList::add);
Stream<String> targetStream = copyList.stream();

의견의 몇 가지 제안을 바탕으로 다음과 같이 사용하려고했습니다 Stream.generate.

public static void main(String[] args) throws Exception {
    Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
    Stream<String> targetStream = Stream.generate(sourceIterator::next);
    targetStream.forEach(System.out::println);
}

그러나 NoSuchElementException(의 호출이 없기 때문에 hasNext)

Exception in thread "main" java.util.NoSuchElementException
    at java.util.AbstractList$Itr.next(AbstractList.java:364)
    at Main$$Lambda$1/1175962212.get(Unknown Source)
    at java.util.stream.StreamSpliterators$InfiniteSupplyingSpliterator$OfRef.tryAdvance(StreamSpliterators.java:1351)
    at java.util.Spliterator.forEachRemaining(Spliterator.java:326)
    at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
    at Main.main(Main.java:20)

나는 살펴 보았다 StreamSupportCollections하지만 난 아무것도 찾지 못했습니다.



3
@DmitryGinzburg euh "무한한"스트림을 만들고 싶지 않습니다.
gontard

1
@DmitryGinzburg Stream.generate(iterator::next)는 작동합니까?
gontard

1
@DmitryGinzburg 유한 반복자에서는 작동하지 않습니다.
assylias

답변:


543

한 가지 방법은 Iterator에서 Spliterator를 만들어 스트림의 기초로 사용하는 것입니다.

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Stream<String> targetStream = StreamSupport.stream(
          Spliterators.spliteratorUnknownSize(sourceIterator, Spliterator.ORDERED),
          false);

더 읽기 쉬운 대안은 Iterable을 사용하는 것입니다. Iterable은 기능적인 인터페이스이기 때문에 Iterator에서 Iterable을 만드는 것은 람다로 매우 쉽습니다.

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();

Iterable<String> iterable = () -> sourceIterator;
Stream<String> targetStream = StreamSupport.stream(iterable.spliterator(), false);

26
스트림이 게으르다 : 코드는 Stream을 반복자와 연결하지만 터미널 작업까지 실제 반복은 일어나지 않는다. 그 동안 반복기를 사용하면 예상 된 결과를 얻지 못합니다. 예를 들어 sourceIterator.next()스트림을 사용하기 전에 소개를 도입 하면 효과가 나타납니다 (첫 번째 항목은 스트림에 표시되지 않음).
assylias

9
@assylias, 그래 정말 멋지다! 어쩌면 당신은 미래 독자들에게이 매우 마술적인 줄을 설명 할 수있을 것 Iterable<String> iterable = () -> sourceIterator;입니다. 이해하는데 시간이 걸렸다는 것을 인정해야합니다.
gontard

7
내가 찾은 것을 말해야합니다. 하나의 추상 메소드 만 Iterable<T>있는 FunctionalInterface입니다 iterator(). 인스턴스를 익명 구현으로 () -> sourceIterator인스턴스화하는 람다 식도 마찬가지 입니다 Iterable.
Jin Kwon

13
다시 한 번 () -> sourceIterator;축약 형new Iterable<>() { @Override public Iterator<String> iterator() { return sourceIterator; } }
Jin Kwon

7
@JinKwon 익명 클래스의 축소 된 형식은 아니지만 (범위 및 컴파일 방법과 같은 약간의 미묘한 차이점이 있음)이 경우에도 유사하게 동작합니다.
assylias

122

버전 21부터 Guava 라이브러리는 Streams.stream(iterator)

@ assylias답변이 보여주는 것 입니다.



JDK가 기본 단일 라이너를 지원할 때까지 이것을 일관되게 사용하는 것이 좋습니다. 미래에 다른 곳에서 보여지는 순수한 JDK 솔루션보다 이것을 찾는 것이 훨씬 간단 할 것입니다 (따라서 리팩토링).
drekbour

이것은 훌륭하지만 ... Java에는 기본 반복자와 스트림이 어떻게 포함되어 있습니까? 그러나 기본 제공되는 간단한 방법은 없습니다. 내 의견으로는 상당히 생략되었습니다.
Dan Lenski

92

좋은 제안! 재사용 가능한 테이크는 다음과 같습니다.

public class StreamUtils {

    public static <T> Stream<T> asStream(Iterator<T> sourceIterator) {
        return asStream(sourceIterator, false);
    }

    public static <T> Stream<T> asStream(Iterator<T> sourceIterator, boolean parallel) {
        Iterable<T> iterable = () -> sourceIterator;
        return StreamSupport.stream(iterable.spliterator(), parallel);
    }
}

그리고 사용법 (정적으로 asStream을 가져 오십시오) :

List<String> aPrefixedStrings = asStream(sourceIterator)
                .filter(t -> t.startsWith("A"))
                .collect(toList());

43

이것은 Java 9에서 가능합니다.

Stream.generate(() -> null)
    .takeWhile(x -> iterator.hasNext())
    .map(n -> iterator.next())
    .forEach(System.out::println);

1
단순하고 효율적이며 서브 클래 싱에 의지하지 않고이 답변을 받아 들여야합니다!
martyglaubitz

1
불행히도 이들은 .parallel()스트림 과 함께 작동하지 않는 것 같습니다 . 또한 Spliterator순차적으로 사용하더라도 넘어가는 것보다 약간 느립니다 .
Thomas Ahle

또한 반복자가 비어 있으면 첫 번째 방법이 발생합니다. 두 번째 방법은 현재 작동하지만 map 및 take의 함수 요구 사항을 위반하지 않고 상태 비 저장 상태이므로 프로덕션 코드에서 주저하지 않습니다.
Hans-Peter Störr

실제로 이것은 허용되는 답변이어야합니다. parallel펑키 해도 단순함은 놀랍습니다.
Sven

11

만들기 Spliterator에서 Iterator사용 Spliterators여기에 사용하고, 예를 들어, 클래스가 spliterator를 만들기위한 하나 개 이상의 기능을 포함하고 spliteratorUnknownSize매개 변수로 반복자를 받고있는 다음 스트림 사용하여 작성StreamSupport

Spliterator<Model> spliterator = Spliterators.spliteratorUnknownSize(
        iterator, Spliterator.NONNULL);
Stream<Model> stream = StreamSupport.stream(spliterator, false);

1
import com.google.common.collect.Streams;

그리고 사용 Streams.stream(iterator):

Streams.stream(iterator)
       .map(v-> function(v))
       .collect(Collectors.toList());

-4

사용하다 Collections.list(iterator).stream()...


6
짧지 만 성능이 매우 낮습니다.
Olivier Grégoire

2
이것은 전체 반복자를 Java 객체로 펼친 다음 스트림으로 변환합니다. 나는 그것을 제안하지 않는다
iec2011007

3
이것은 반복자가 아닌 열거에만 해당되는 것으로 보입니다.
john16384

1
일반적으로 끔찍한 대답은 아니며, 핀치에 유용하지만 질문은 성능을 언급하고 대답은 성과가 아닙니다.
Sled
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.