イテレータをストリームに変換する方法は?


468

私は、イテレータをストリームに「見る」Iteratorために、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ええと、私は「無限」のストリームを作成したくありません。
gontard 14

1
@DmitryGinzburgはStream.generate(iterator::next)機能しますか?
gontard 14

1
@DmitryGinzburgこれは有限のイテレータでは機能しません。
アッシリア14

回答:


543

1つの方法は、イテレーターからスプリッターを作成し、それをストリームの基礎として使用することです。

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
ストリームは遅延します。コードはストリームをイテレータにリンクするだけですが、実際の反復はターミナル操作まで発生しません。その間にイテレータを使用すると、期待した結果が得られません。たとえばsourceIterator.next()、ストリームを使用する前にを導入すると、効果が表示されます(最初のアイテムはストリームでは表示されません)。
アッシリア14

9
@assylias、ええ、それは本当に素晴らしいです!多分あなたは将来の読者のためにこの非常に魔法のような線を説明することができますIterable<String> iterable = () -> sourceIterator;。理解するのに少し時間がかかったことを認めざるを得ません。
gontard 14

7
私が見つけたものを教えてください。Iterable<T>は、FunctionalInterface抽象メソッドが1つしかないiterator()です。だから、() -> sourceIteratorインスタンス化ラムダ式であるIterable匿名の実装としてインスタンスが。
Jin Kwon

13
再び、() -> sourceIterator;短縮形ですnew Iterable<>() { @Override public Iterator<String> iterator() { return sourceIterator; } }
ジン・クォン

7
@JinKwonこれは実際には匿名クラスの短縮形ではありません(スコープやコンパイル方法などのわずかな違いがあります)が、この場合も同様に動作します。
アッシリア

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

また、イテレータが空の場合、最初のメソッドがスローされます。2番目の方法は今のところ機能しますが、mapおよびtakeWhileの関数の要件に違反するため、ステートレスになるため、本番用コードでそれを行うことをためらいます。
Hans-PeterStörr

本当に、これは受け入れられる答えであるべきです。にもかかわらず、parallelシンプルさ、ファンキーであるかもしれない素晴らしいです。
スベン

11

Create Spliteratorfrom Iteratorusing Spliteratorsクラスには、スプリッターを作成するための複数の関数が含まれています。たとえば、ここspliteratorUnknownSizeでは、イテレーターをパラメーターとして取得し、次にStream usingを作成しています。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
短い間、これは非常に低いパフォーマンスです。
OlivierGrégoire18年

2
これにより、イテレータ全体がJavaオブジェクトに展開され、ストリームに変換されます。お勧めし
ませ

3
これは列挙型のみであり、反復子ではないようです。
john16384

1
一般的にひどい答えではなく、ピンチで役立ちますが、質問はパフォーマンスに言及しており、答えはパフォーマンスが良くありません。
そり
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.