Iterable <T>がstream()およびparallelStream()メソッドを提供しないのはなぜですか?


241

Iterableインターフェイスがstream()and parallelStream()メソッドを提供しないのはなぜですか。次のクラスについて考えてみます。

public class Hand implements Iterable<Card> {
    private final List<Card> list = new ArrayList<>();
    private final int capacity;

    //...

    @Override
    public Iterator<Card> iterator() {
        return list.iterator();
    }
}

トレーディングカードゲームのプレイ中にカードを手にできるので、これはハンドの実装です。

基本的にはをラップしList<Card>、最大容量を確保し、その他の便利な機能をいくつか提供します。として直接実装するよりも優れていList<Card>ます。

さて、便宜上、を実装した方がいいと思ったのでIterable<Card>、ループを拡張したい場合は拡張forループを使用できます。(私のHandクラスもget(int index)メソッドを提供しているため、Iterable<Card>私の意見では正当化されます。)

Iterableインターフェースは、(Javadocを除外)以下を提供します:

public interface Iterable<T> {
    Iterator<T> iterator();

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

これでストリームを取得できます:

Stream<Hand> stream = StreamSupport.stream(hand.spliterator(), false);

本当の質問に:

  • Iterable<T>を実装するデフォルトのメソッドを提供しないのはなぜですか?これを不可能または望ましくないものにするものは何もありませんか?stream()parallelStream()

ただし、関連する質問は次のとおりです。なぜStream <T>はIterable <T>を実装しないのですか?
奇妙なことに、逆方向にやることを示唆しています。


1
これはLambdaメーリングリストにとって良い質問だと思います。
Edwin Dalorzo 2014

ストリームを反復処理したいのはなぜ奇妙なことですか?他にどのようにしbreak;て反復を行うことができますか?(Stream.findFirst()
わかりまし

実用的な回避策については、Java 8 JDK使用してIterableをストリームに変換するも参照してください。
Vadzim

回答:


298

これは省略ではありませんでした。2013年6月にEGリストについて詳細な議論がありました。

専門家グループの決定的な議論は、このスレッドに根ざしています

(最初は専門家グループにとってさえ)「明白」であるstream()ように見えましIterableIterableが、それほど一般的であるという事実が問題になりました。

Stream<T> stream()

いつもあなたが望んでいたものではありませんでした。たとえば、Iterable<Integer>ストリームメソッドにを返すようにしたいものもありIntStreamます。しかし、stream()メソッドをこの階層の上位に置くと、それは不可能になります。したがって、代わりに、メソッドを提供することで、Streamからを簡単に作成できるようにしました。in の実装は次のとおりです。Iterablespliterator()stream()Collection

default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

すべてのクライアントは、次のものを使用して必要なストリームを取得できますIterable

Stream s = StreamSupport.stream(iter.spliterator(), false);

最後に、我々は、追加と結論づけstream()にはIterable間違いです。


8
まず、質問にお答えいただきありがとうございます。私はなぜものの、まだ興味がありますIterable<Integer>(私はあなたが話していると思う?)を返すようにしたいと思いますIntStream。その場合、イテラブルはむしろではPrimitiveIterator.OfIntないでしょうか?または、おそらく別のユースケースを意味しますか?
skiwi 2014

139
上記のロジックがIterableに適用されたと思われるのは奇妙です(誰かがIntStreamを返すようにしたいのでstream()を使用することはできません)が、まったく同じメソッドをCollectionに追加することについて同じ量の考えが与えられていません( Collection <Integer>のstream()がIntStreamも返すようにしたい場合があります。両方に存在していたか、両方に存在していなかったとしても、人々はおそらく自分の生活に慣れているだけでしょう。もう1つは、かなりの省略になります...
Trejkaz

6
ブライアン・マッカッション:それは私にはもっと理にかなっています。人々は論争に飽き飽きして、それを安全にプレーすることに決めたように思えます。
ジョナサンロック

44
これは理にかなってStream.of(Iterable)いますが、少なくともAPIドキュメントを読んでメソッドを合理的に発見できるようにする代替staticがない理由はありますか-Streamsの内部で実際に作業したことがない人としては、ドキュメントでは「ほとんどがライブラリライタ向け」である「低レベルの操作」を提供するものとして説明されているも調べましたStreamSupport
Jules

9
私はジュールに完全に同意します。StreamSupport.stream(iter.spliterator()、false);の代わりに、静的メソッドStream.of(Iteratable iter)またはStream.of(Iterator iter)を追加する必要があります。
user_3380739 16

23

ラムダプロジェクトのメーリングリストのいくつかで調査を行いましたが、いくつか興味深い議論があったと思います。

これまでのところ、十分な説明はありません。これらすべてを読んだ後、私はそれが単なる漏れであると結論付けました。しかし、ここでは、APIの設計中に何年にもわたって何度も議論されたことがわかります。

Lambda Libs Specエキスパート

Lambda Libs Spec Expertsメーリングリストでこれについての議論を見つけました:

Iterable / Iterator.stream()の下で、Sam Pullaraは次のように述べています。

制限/サブストリーム機能[1]がどのように実装されるかを確認するためにブライアンと協力していましたが、イテレータへの変換がそれを行う正しい方法であると彼は提案しました。私はその解決策について考えていましたが、イテレータを取得してストリームに変換する明確な方法が見つかりませんでした。それがそこにあることがわかり、最初にイテレーターをスプリッターに変換し、次にスプリッターをストリームに変換する必要があるだけです。したがって、これにより、Iterable / Iteratorの1つを直接、または両方にぶら下げる必要があるかどうかを再確認する必要があります。

私の提案は、少なくともIteratorでそれを使用して、2つの世界の間をきれいに移動できるようにすることです。また、実行する必要がなく、簡単に発見できます。

Streams.stream(Spliterators.spliteratorUnknownSize(iterator、Spliterator.ORDERED))

そして、ブライアン・ゲッツは答えた

Samのポイントは、イテレーターを提供するライブラリクラスはたくさんあるが、必ずしも独自のスプリッターを作成できるわけではないということでした。だからあなたができることはstream(spliteratorUnknownSize(iterator))を呼び出すことだけです。サムは、あなたのためにIterator.stream()を定義することを提案しています。

stream()メソッドとspliterator()メソッドをライブラリライター/上級ユーザー向けに維持したいと思います。

以降

「スプリテレーターを書く方がイテレーターを書くよりも簡単であることを考えると、イテレーターの代わりにスプリッターを書くほうが好きです(イテレーターはとても90年代です:)」

しかし、あなたは要点を逃しています。すでに何十億ものクラスがあり、すでにイテレータを提供しています。そして、それらの多くはスプリッターに対応していません。

Lambdaメーリングリストでの以前のディスカッション

これはあなたが探している答えではないかもしれませんが、Project Lambdaメーリングリストでこれについて簡単に説明しました。おそらくこれは、このテーマに関するより幅広い議論を促進するのに役立ちます。

Iterableからのストリームの下のBrian Goetzの言葉では:

後退しています...

ストリームを作成する方法はたくさんあります。要素の記述方法に関する情報が多いほど、ストリームライブラリが提供する機能とパフォーマンスが向上します。情報の少ないものから順に、以下のとおりです。

イテレータ

イテレータ+サイズ

スプリッター

そのサイズを知っているスプリッター

そのサイズを知っていて、さらにすべてのサブスプリットがそのサイズを知っていることを知っているスプリッター。

(Q(要素ごとの作業)が自明ではない場合、ダムイテレータからでも並列処理を抽出できることに気付く人もいます。)

Iterableにstream()メソッドがある場合、サイズ情報なしでIteratorをSpliteratorでラップするだけです。しかし、反復処理可能ですほとんどの事はやるサイズ情報を持っています。つまり、不十分なストリームを提供していることになります。それはあまり良くありません。

ここでStephenが概説している、APIではなくCollectionの代わりにIterableを受け入れることの欠点の1つは、「小さなパイプ」を介して物事を強制しているため、有用な場合はサイズ情報を破棄することです。forEachを実行するだけであれば問題ありませんが、さらに多くのことを実行したい場合は、必要なすべての情報を保持できるほうが適切です。

Iterableが提供するデフォルトは、実際にはひどいものです。Iterableの大多数がその情報を知っていても、サイズは破棄されます。

矛盾?

ただし、この議論は、最初はイテレータに基づいていたストリームの初期設計に対して専門家グループが行った変更に基づいているようです。

それでも、コレクションのようなインターフェイスでは、ストリームメソッドが次のように定義されていることに気付くことは興味深いです。

default Stream<E> stream() {
   return StreamSupport.stream(spliterator(), false);
}

これは、Iterableインターフェースで使用されているコードとまったく同じになる可能性があります。

だから、私がこの答えはおそらく満足のいくものではないが、それでも議論にとって興味深いと私が言った理由です。

リファクタリングの証拠

メーリングリストでの分析を続けると、splitIteratorメソッドはもともとCollectionインターフェースにあったようで、2013年のある時点で、Iterableに移動しました。

splitIteratorをCollectionからIterableに引き上げます。

結論/理論?

次に、SplitIteratorをCollectionからIterableに移動したときにストリームメソッドも移動する必要があったため、Iterableにメソッドがないことは単なる省略である可能性があります。

他の理由がある場合、それらは明白ではありません。他の誰かが他の理論を持っていますか?


お返事には感謝しますが、その理由には同意できません。あなたがオーバーライドし、その時点でspliterator()Iterable、すべての問題が固定されている、とあなたは自明実装できるstream()parallelStream()...
skiwi

@skiwiだから私はこれがおそらく答えではないと言ったのです。専門家グループが彼らがした決定をした理由を知るのは難しいので、私は議論に追加しようとしています。私たちができることは、メーリングリストでフォレンジックを行って、何らかの理由が考えられるかどうかを確認することです。
エドウィンダロルゾ2014

1
@skiwi私は他のメーリングリストをレビューし、議論のより多くの証拠とおそらくいくつかの診断の理論化に役立ついくつかのアイデアを見つけました。
Edwin Dalorzo 2014

あなたの努力のおかげで、私はそれらのメーリングリストを効率的に分割する方法を本当に学ぶべきです。フォーラムなどの現代的な方法で視覚化できると助かります。引用符が含まれたプレーンテキストの電子メールを読むのは効率的ではありません。
skiwi 2014

6

メソッドjava.util.Collectionを提供する使用可能なサイズがわかっている場合stream()

public class Hand extends AbstractCollection<Card> {
   private final List<Card> list = new ArrayList<>();
   private final int capacity;

   //...

   @Override
   public Iterator<Card> iterator() {
       return list.iterator();
   }

   @Override
   public int size() {
      return list.size();
   }
}

その後:

new Hand().stream().map(...)

同じ問題に直面し、メソッドを追加するだけで実装を簡単に実装Iterableに拡張できることに驚いていました(幸いにもコレクションのサイズがありました:-)。AbstractCollectionsize()

オーバーライドも検討する必要がありますSpliterator<E> spliterator()

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