web-dev-qa-db-ja.com

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

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

Iterator<T> iterator()

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

Foreachループを使用してStreamを反復処理する場合、次のようなことをする必要があります

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)) { ... }

ここに何かが足りませんか?

245
roim

すでに同じ質問が寄せられています メーリングリスト上 ☺。主な理由は、Iterableには反復可能なセマンティックがありますが、Streamにはないということです。

主な理由は、Iterableが再利用性を意味するのに対し、Streamは一度しか使用できないもので、より多くの場合はIteratorのようだと思います。

StreamIterableを拡張した場合、既存のコードがfor (element : iterable)を2回目にIterableをスローするExceptionを受け取ったときに驚くかもしれません。

186
kennytm

StreamIterableに変換するには、次のようにします。

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

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

void foo(Iterable<X> iterable)

単に

foo(stream::iterator) 

ただし、おそらく面白そうです。もう少し明示的にする方が良いかもしれません

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

StreamExIterable(およびStream)と、Streamにない他の非常に素晴らしい機能のホストを実装していることを指摘したいと思います。

10

kennytmの説明StreamIterableとして扱うのが安全でない理由、および Zhong Yuが回避策を提供Streamの使用を許可する安全ではありませんが、Iterableのように。 Iterable仕様によって行われたすべての保証を満たすStreamから再利用可能なIterableの両方の長所を得ることができます。

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

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

1つの大きな欠点があります。

遅延反復の利点は失われます。現在のスレッドのすべての値をすぐに反復する場合、オーバーヘッドは無視できます。ただし、部分的にのみ、または別のスレッドで反復することを計画している場合、この即時の完全な反復により、意図しない結果が生じる可能性があります。

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

7
Zenexer

次のように、forループでStreamを使用できます。

Stream<T> stream = ...;

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

(実行 このスニペットはこちら

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

(これは上記のコメントの一部でカバーされています(例: Aleksandr Dubinsky )が、よりわかりやすくするために回答に引き出したいと思いました。)

4
Rich

StreamIterableを実装しません。 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
Ashish Tyagi

サードパーティのライブラリを使用してもかまわない場合 cyclops-react は、StreamとIterableの両方を実装し、再生可能なストリームを定義します(問題の解決 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のリード開発者です]

2
John McClean

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

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

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

    // ...
}
0
BullyWiiPlaza