web-dev-qa-db-ja.com

通常、Collectionsを返す場所にStreamsを返すのは常識的なことですか?

レガシーコードに関連付けられていないAPIを開発しているときに、結果を収集することによって純粋にStreamsパイプラインで終了するメソッドを作成していることがよくあります。このように:

_ImmutableSet<T> deriveSomethingMeaningfulFromPrivateState() {
    return myPrivateThingies.stream()
        .map(this::ownerOfThing)
        .map(Owner::socialStatus)
        .filter(SocialStatus::isHeAFineMatey)
        .collect(MyCustomCollectors.toImmutableSet());
}
_

現在、このクラスのほとんどのクライアントは、通常で要素を検索して反復処理するためにコレクション(この場合はImmutableSet)を必要としますが、クライアントは、ストリームを使用することでメリットを得ることができるため、コレクションから新しいストリームを取得する必要なく、そのストリーム上にさらにいくつかの操作をパイプすることができます。したがって、ストリームを返すことで、コレクションを持っている場合に備えていたオプションのスーパーセットをクライアントに提供します(結局のところ、ストリーム自体を常にcollect()することができます:

_Stream<T> deriveSomethingMeaningfulFromPrivateState() {
    return myPrivateThingies.stream()
        .map(this::ownerOfthing)
        .map(Owner::socialStatus)
        .filter(SocialStatus::isHeAFineMatey);
        // No collect
}
_

このアプローチは、潜在的な欠陥が見当たらないため、試してみるのが魅力的です。しかし、どのライブラリでもこのアプローチを見たことがありません(おそらくJava 8)の登場後にリリースされたライブラリが多くなかったため)、私はそれを採用するのが少し怖いです。既存のライブラリクラスは、通常、プライベート状態から派生したときにコレクションを返します。

発生する可能性のある何か悪いことはありますか Java-8以前の自分がコレクションを返す場所にストリームを返すことにした場合?それとも、私はここで私的な状態から派生したものすべてでアンチパターンの何かをしていますか?

19
jojman

myPrivateThingiesが変更可能である場合は、プライベート状態とストリーム結果の間に非表示の依存関係が作成されています。クライアントが間接的にmyPrivateThingiesの状態を変化させる可能性がある場合、collectを呼び出したときに、本来意図したものとは異なる結果が得られます。

myPrivateThingiesが不変の場合、結果は参照透過的になりますが、注意が必要なもう1つの問題があります。 semantic garbage 、つまり、大量のメモリを保持する不要になりました。 myPrivateThingiesが非常に大きく、ストリームの収集結果が小さいとします。クライアントは、ストリームを生成したオブジェクトへのすべての参照を破棄した後もずっとストリームを保持している可能性がありますが、streammyPrivateThingiesがガベージコレクションの対象にならないようにしています。結果を熱心に収集すると、myPrivateThingiesが解放されます。

これは、実際にJava 7の前にsubstringを呼び出すときに発生しました。オラクルは、部分文字列を毎回コピーしないことによる潜在的な効率の節約は、平均的なユーザーを過度に驚かす価値がないと判断しましたメモリの消費。つまり、古い動作(パーサーなど)の実際のユースケースがなかったわけではありませんが、多くの場合、熱心に結果を収集するのは十分高速であり、その場合、長所と潜在的な欠点がありません。

一方、ストリームを返すことで、クライアントは、データ構造を選択するのではなく、結果を保持するために使用するデータ構造を選択できます。両方のオプションを提供する価値があるかもしれません。

14
Doval

考慮すべき最も重要なこと:Streamsは1回しか反復できませんが、Collectionよりも柔軟性が高いので、さらにStreamsまたはIteratorsは、結果に対して追加の繰り返し処理を実行します。

したがって、メソッドの呼び出し元が結果を1回だけ使用するかどうかわからない場合は、Collectionを返すことをお勧めします。


サンプルコードに明らかなエラーが1つあります。なぜSocialStatusheという人物の概念があるのでしょうか。

4
h.j.k.

私の見解では、違います。ストリームで実行できることは、コレクションで実行できることの厳密なスーパーセットであり、多くの場合、効率を上げることができるため、不慣れでない限り、それらを使用しない理由はありません。 「ラムダ式はJava 8へのゲートウェイドラッグですが、ストリームは本当の中毒です。」(Venkat Subramaniam、Javaでの関数型プログラミング

3
Kilian Foth