Stream <T>がIterable <T>を実装しないのはなぜですか?


262

Java 8では、クラスStream <T>があり、奇妙なことにメソッドがあります

Iterator<T> iterator()

そのため、このメソッドを必要とするIterable <T>インターフェースの実装が期待されますが、そうではありません。

foreachループを使用してストリームを反復処理する場合は、次のようにする必要があります

public static Iterable<T> getIterable(Stream<T> s) {
    return new Iterable<T> {
        @Override
        public Iterator<T> iterator() {
            return s.iterator();
        }
    };
}

for (T element : getIterable(s)) { ... }

ここで何か不足していますか?


7
iterableの他の2つのメソッド(forEachおよびspliterator)もストリームにあることは言うまでもありません
njzk2

1
これはStream、期待するレガシーAPI に渡すために必要ですIterable
ZhongYu 2013年

12
良いIDE(例えばIntelliJのは)にコードを簡素化するよう求められますgetIterable()するreturn s::iterator;
ZhongYu

24
メソッドはまったく必要ありません。ストリームがあり、Iterableが必要な場合は、stream :: iterator(または、必要に応じて()-> stream.iterator())を渡すだけで完了です。
Brian Goetz

7
残念ながら私は書くことができないfor (T element : stream::iterator)ので、StreamもIterableメソッドまたはメソッドを実装するのがよいと思いtoIterable()ます。
トルステン

回答:


197

人々はすでに同じことをメーリングリストで質問していますhave 。主な理由は、Iterableにもre-iterableセマンティクスがあるのに対し、Streamにはないということです。

主な理由は、それIterableが再利用性を意味するのに対して、Stream一度だけしか使用できないものであると思いますIterator

場合はStream、拡張Iterableは、受信したときに、既存のコードは驚くかもしれませんIterableスローそのException彼らが二度目にfor (element : iterable)


22
奇妙なことに、Java 7のこの動作にはすでにいくつかのイテラブルがありました。イテレータメソッドを呼び出して2番目以降のイテレータを取得すると、IllegalStateExceptionがスローされます。(openjdk.java.net/projects/nio/javadoc/java/nio/file/...
roim

32
残念ながら、常に複数回呼び出し可能である必要があるIterableかどうかについてのドキュメントはiteratorありません。それは彼らがそこに入れるべきものです。これは正式な仕様というよりは標準的な方法のようです。
Lii 14

7
彼らが言い訳を使うつもりなら、少なくともasIterable()メソッド、またはIterableのみを取るすべてのメソッドのオーバーロードを追加できると思います。
Trejkaz 2014

26
たぶん、最善の解決策は、JavaのforeachがIterable <T>だけでなくStream <T>も受け入れるようにすることでしょうか?
食欲をそそるエリジウム2014

3
@Lii標準的な方法としては、かなり強力です。
ビジクロップ2015年

161

をに変換するStreamにはIterable、次のようにします

Stream<X> stream = null;
Iterable<X> iterable = stream::iterator

Stream期待するメソッドにを渡すにはIterable

void foo(Iterable<X> iterable)

単に

foo(stream::iterator) 

しかし、おそらくおかしく見えます。もう少し明確にする方がいいかもしれません

foo( (Iterable<X>)stream::iterator );

67
for(X x : (Iterable<X>)stream::iterator)見た目は醜いですが、これをループで使用することもできます。本当に、全体の状況はばかげています。
Aleksandr Dubinsky 2013年

24
@HRJIntStream.range(0,N).forEach(System.out::println)
MikeFHay 2014

11
この文脈での二重コロン構文は理解できません。違いは何だstream::iteratorstream.iterator()の元許容なり、Iterable後者ではありませんか?
ダニエルC.ソブラル2014

20
自分に答える:Iterable関数型のインターフェイスなので、それを実装する関数を渡すだけで十分です。
ダニエルC.ソブラル2014

5
私は@AleksandrDubinskyに同意する必要があります。実際の問題は、(X x:(Iterable <X>)stream :: iterator)の回避策があることです。これにより、コードノイズが大きくても同じ操作が可能になります。しかし、これはJavaであり、長い間List <String> list = new ArrayList(Arrays.asList(array))を許容してきました:)できる能力は、すべてのList <String> list = arrayを提供するだけです。 toArrayList()。しかし、本当の不条理は、すべての人がforEach on Streamを使用するように促すことで、例外を必ずオフにする必要があるということです。 (当然終了)。
コーディネーター


8

for次のように、ループでストリームを使用できます。

Stream<T> stream = ...;

for (T x : (Iterable<T>) stream::iterator) {
    ...
}

(実行 このスニペットをここでてください

(これはJava 8機能インターフェースキャストを使用します。)

(これは上記のコメントの一部(例:Aleksandr Dubinsky)でカバーされていますが、それを解答に引き出してもっと見やすくしたいと思いました。)


ほとんどの人は、コンパイル方法さえ理解する前に、2回または3回見る必要があります。ばかげています。(これは同じコメントストリームのコメントへの応答でカバーされていますが、コメントをより見やすくするためにここに追加したいと思いました。)
ShioT

7

kennytmは、aをStreamとして扱うことが安全ではない理由を説明しIterableZhong Yuは、 a Streamをinとして使用することをIterable安全でない方法で許可する回避策提供しました。両方の世界のベストを得ることが可能です:仕様で行われたすべての保証を満たすIterableから再利用StreamできIterableます。

注:SomeTypeはここではタイプパラメータではありません。適切なタイプ(たとえばString)に置き換えるか、リフレクションを使用する必要があります

Stream<SomeType> stream = ...;
Iterable<SomeType> iterable = stream.collect(toList()):

主な欠点が1つあります。

遅延イテレーションのメリットは失われます。現在のスレッドのすべての値をすぐに反復することを計画した場合、オーバーヘッドは無視できます。ただし、部分的または別のスレッドでのみ反復することを計画した場合、この即時かつ完全な反復は意図しない結果をもたらす可能性があります。

もちろん、大きな利点は、を再利用できることですがIterable、1回し(Iterable<SomeType>) stream::iteratorか使用できません。受信コードがコレクションを何度も繰り返す場合、これは必要なだけでなく、パフォーマンスにとっても有益です。


1
答える前にコードをコンパイルしようとしましたか?動かない。
Tagir Valeev

1
@TagirValeevはい、そうしました。Tを適切なタイプに置き換える必要があります。作業コードから例をコピーしました。
Zenexer 2016年

2
@TagirValeev IntelliJでもう一度テストしました。IntelliJはこの構文に混乱することがあります。私はそれに対するパターンを本当に見つけていません。ただし、コードは正常にコンパイルされ、IntelliJはコンパイル後にエラー通知を削除します。それは単なるバグだと思います。
Zenexer 2016年

1
Stream.toArray()は配列ではなく配列を返すIterableため、このコードはまだコンパイルされません。しかし、IntelliJがコンパイルしているように見えるため、Eclipseのバグである可能性があります
benz

1
@Zenexerどのようにして配列をIterableに割り当てることができましたか?
radiantRazor 2017

3

Stream実装していませんIterable。の一般的な理解Iterableは、何度も何度も繰り返すことができるものです。Stream再生できない場合があります。

私が考えることができる唯一の回避策は、ストリームに基づくイテラブルも再生可能である場合、ストリームを再作成することです。Supplier新しいイテレータが作成されるたびに、ストリームの新しいインスタンスを作成するために以下を使用しています。

    Supplier<Stream<Integer>> streamSupplier = () -> Stream.of(10);
    Iterable<Integer> iterable = () -> streamSupplier.get().iterator();
    for(int i : iterable) {
        System.out.println(i);
    }
    // Can iterate again
    for(int i : iterable) {
        System.out.println(i);
    }

2

サードパーティのライブラリを使用することを気にしない場合、cyclops-reactは、StreamとIterableの両方を実装し、再生も可能なStreamを定義します(記述されたkennytmの問題の解決)。

 Stream<String> stream = ReactiveSeq.of("hello","world")
                                    .map(s->"prefix-"+s);

または:-

 Iterable<String> stream = ReactiveSeq.of("hello","world")
                                      .map(s->"prefix-"+s);

 stream.forEach(System.out::println);
 stream.forEach(System.out::println);

[開示私はc​​yclops-reactの主要開発者です]


0

完璧ではありませんが、動作します:

iterable = stream.collect(Collectors.toList());

それは、ストリームからすべてのアイテムを取得し、その中にそれらを配置しますので、完璧ではないList正確に何をされていない、IterableそしてStream約あります。彼らは怠惰であることになっています。


-1

次のStream<Path>ようにして、フォルダー内のすべてのファイルを反復処理できます。

Path path = Paths.get("...");
Stream<Path> files = Files.list(path);

for (Iterator<Path> it = files.iterator(); it.hasNext(); )
{
    Object file = it.next();

    // ...
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.