Javaでは、ループよりもストリームの利点は何ですか?[閉まっている]


133

私はインタビューでこれを尋ねられました、そして私が私が持つことができる最良の答えを与えたと確信していません。並列検索を行うことができ、null値は私が覚えていない方法で処理されたと述べました。今、私はオプションを考えていたことがわかります。ここで何が欠けていますか?彼らはそれがより良いかより簡潔なコードであると主張していますが、私が同意するかどうかはわかりません。


それがどれほど簡潔に回答されたかを考えると、これは結局のところ、質問の幅が広すぎなかったようです。


彼らがインタビューでこの質問をしている場合、そして明らかにそうである場合、答えを見つけるのを難しくする以外に、それを破壊する目的は何ですか?つまり、何を探しているのですか?私は質問を分解してすべてのサブ質問に回答させることができますが、すべてのサブ質問へのリンクを含む親質問を作成します...しかしかなりばかげているようです。その間、あまり一般的でない質問の例を挙げてください。この質問の一部だけを尋ねても、意味のある答えを得る方法は知りません。同じ質問を別の方法で行うこともできます。たとえば、「ストリームにはどのような目的がありますか?」または「forループの代わりにストリームを使用するのはいつですか?」または「なぜforループの代わりにストリームを使用するのですか?」これらはすべてまったく同じ質問ですが。

...または誰かが非常に長いマルチポイントの回答をしたため、広すぎると考えられますか?率直に言って、知っている人ならだれでも、事実上どんな質問でもそれを行うことができます。たとえば、あなたがたまたまJVMの作者の1人である場合、私たちのほとんどができなかった1日中forループについて話すことができます。

「質問を編集して、適切な回答を特定するのに十分な詳細のある特定の問題に限定してください。複数の個別の質問を一度に行うことは避けてください。この質問を明確にするために、質問ページを参照してください。」

以下に示すように、適切な回答が提供されており、1つあり、提供するのが簡単であることを証明しています。


7
これは私見の意見に基づいています。個人的には、コードを読みやすくするためにストリームを好みます。それはあなたがどのようにではなくあなたが望むものを書くことを可能にします。さらに、ワンライナーで驚くべきことをするのはまったく悪いことです。
Arnaud Denoyelle 2017年

19
30本でもライナー1本?私は長い鎖が好きではありません。
user447607

1
その上、私がここで探しているのは、インタビューに対する適切な応答です。これが重要な唯一の「意見」です。
user447607

1
教育的に言えば、この質問は将来のインタビューで私を多少悪化させました、@ slimは本当にそれを釘付けにしましたが、産業的に言えば、Microsoftプログラミング言語がJava言語を取り去ることでキャリアを築き上げた方法についても話し、最終的にJavaは取り込んで復讐しますラムダ式と対戦相手からのストリームをオフにして、Javaが構造体とユニオンについて将来どうするかを見てみましょう:)
ShayHaned

5
/ - :唯一の関数型プログラミングでは、電力の割合をタップストリーム注
するThorbjörnRavnアンデルセン

回答:


251

興味深いのは、面接の質問では、不利な点について尋ねることなく、長所について尋ねていることです。

ストリームはより宣言的なスタイルです。またはより表現力豊かなスタイル。方法を説明するよりも、コードで意図を宣言する方がよいと考えられます。

 return people
     .filter( p -> p.age() < 19)
     .collect(toList());

...リストから一致する要素をフィルタリングしていることを明確に述べていますが、

 List<Person> filtered = new ArrayList<>();
 for(Person p : people) {
     if(p.age() < 19) {
         filtered.add(p);
     }
 }
 return filtered;

「私はループをしている」と言います。ループの目的はロジックの奥深くに埋め込まれています。

多くの場合、ストリームはより簡潔です。同じ例がこれを示しています。Terserは必ずしも優れているとは限りませんが、簡潔さと表現力を同時に兼ね備えていれば、はるかに優れています。

ストリームは関数と強い親和性があります。Java 8はラムダと関数型インターフェースを導入し、強力なテクニックのおもちゃ箱全体を開きます。ストリームは、オブジェクトのシーケンスに関数を適用する最も便利で自然な方法を提供します。

ストリームは、可変性の低下を助長します。これは、関数型プログラミングの側面にある程度関係しています。ストリームを使用して作成するプログラムの種類は、オブジェクトを変更しない種類のプログラムである傾向があります。

ストリームは疎結合を促進します。ストリーム処理コードは、ストリームのソースや最終的な終了方法を知る必要はありません。

ストリームは非常に洗練された動作を簡潔に表現できます。例えば:

 stream.filter(myfilter).findFirst();

ストリーム全体をフィルタリングしているかのように一見すると、最初の要素が返される場合があります。しかし、実際にfindFirst()は操作全体を駆動するため、1つのアイテムが見つかった後は効率的に停止します。

ストリームは、将来の効率向上の余地を提供します。一部の人々はベンチマークを行っており、メモリ内Listのsまたは配列からのシングルスレッドストリームは、同等のループよりも低速になる可能性があることを発見しました。より多くのオブジェクトとオーバーヘッドが出ているため、これはもっともらしいことです。

しかし、ストリームはスケーリングします。並列ストリーム操作に対するJavaの組み込みサポートに加えて、モデルが適合するため、APIとしてStreamsを使用する分散map-reduce用のいくつかのライブラリがあります。

短所?

パフォーマンスfor配列を介したループは、ヒープとCPU使用率の両方の点で非常に軽量です。生の速度とメモリの節約を優先する場合は、ストリームを使用する方が悪いです。

親しみやすさ。世界には、ループに精通していてストリームが斬新である、多くの言語のバックグラウンドを持つ経験豊富な手続き型プログラマーがたくさんいます。一部の環境では、そのような人に馴染みのあるコードを記述したい場合があります。

認知オーバーヘッド。その宣言的な性質、およびその下で起こっていることからの抽象化の増加により、コードと実行との関係の新しいメンタルモデルを構築する必要がある場合があります。実際には、問題が発生した場合、またはパフォーマンスや微妙なバグを深く分析する必要がある場合にのみ、これを行う必要があります。それが「うまくいく」とき、それはうまくいきます。

デバッガーは改善されていますが、今でも、デバッガーでストリームコードをステップ実行している場合、単純なループは従来のデバッガーが使用する変数とコードの場所に非常に近いため、同等のループよりも作業が難しくなる可能性があります。


4
ストリームのようなものがもっと一般的になり、特にFP指向ではない多くの一般的に使用される言語で表示されるようになるのは、きちんとするのは公平だと思います。
ケーシー

5
ここにリストされた長所と短所を考えると、ストリームは、重要ではないパフォーマンスの部分での非常に単純な用途(小さなロジックif / then / else、多くのネストされた呼び出しやラムダなど)以外には価値がないと思います
Henrik Kjus Alstad

1
@HenrikKjusAlstadそれは私が通信することを意図した持ち帰りではありません。ストリームは成熟していて強力で表現力があり、本番環境レベルのコードに完全に適しています。
スリム

ああ、私はそれを本番環境で使用しないという意味ではありませんでした。しかし、むしろ、特に結果のストリームが複雑に見える場合は、ストリームではなく、古い形式のループ/ ifなどにデフォルト設定します。ストリームがループをビートする用途があると私は確信していますが、はっきりしていれば、たいていの場合、それは「タイド」であると思います。したがって、私は古い方法に固執するための認知的オーバーヘッドの議論に重点を置きます。
Henrik Kjus Alstad

1
@lijepdam-しかし、「リストを反復する」がコードの中心的な目的ではない場合、「このリストを反復処理しています(理由を調べるにはループ内を参照)」というコードがまだあります。
スリムな

16

構文の面白さはさておき、Streamsは潜在的に無限に大きいデータセットで動作するように設計されていますが、配列、コレクション、およびIterableを実装するほぼすべてのJava SEクラスは完全にメモリ内にあります。

ストリームの欠点は、フィルター、マッピングなどがチェック例外をスローできないことです。このため、ストリームは、中間のI / O操作などには不適切な選択になります。


7
もちろん、無限のソースをループすることもできます。
2017年

しかし、処理する要素がDBに永続化されている場合、Streamsをどのように使用しますか?ジュニア開発者は、ストリームを使用するためだけにコレクション内のそれらすべてを読み取るように誘惑される可能性があります。そして、それは災害になるでしょう。
Lluis Martinez

2
@LluisMartinez優れたDBクライアントライブラリは、次のようなものを返しますStream<Row>-または、StreamDB結果カーソル操作をラップする独自の実装を作成することが可能です。
スリム

Streamsが例外を黙って無視することは、私が最近噛まれたバグです。直感的ではない。
xxfelixxx

@xxfelixxxストリームは、暗黙的に例外を無視しません。これを実行してみましょう:Arrays.asList("test", null).stream().forEach(s -> System.out.println(s.length()));
VGR

8
  1. 誤って認識した:並列操作はStreamsではなくOptionalsを使用します。

  2. ストリームを操作するメソッドを定義できます。それらをパラメーターとして受け取り、それらを返すなどです。ループをパラメーターとして受け取るメソッドは定義できません。これにより、複雑なストリーム操作を1回実行して、何度も使用することができます。ここでJavaには欠点があることに注意してください。someMethod(stream)ストリームの独自のメソッドではなく、メソッドを呼び出す必要があるstream.someMethod()ため、それらを混合すると読み取りが複雑になります。操作の順序を確認してみてください

    myMethod2(myMethod(stream.transform(...)).filter(...))

    他の多くの言語(C#、Kotlin、Scalaなど)では、ある種の「拡張メソッド」を使用できます。

  3. 順次操作のみが必要で、再利用する必要がないため、ストリームまたはループのいずれかを使用できる場合でも、ストリームに対する単純な操作は、ループ内の非常に複雑な変更に対応する場合があります。


説明1.オプションのインターフェースは、ヌルがチェーンで処理される手段ではありませんか?3に関しては、短絡されたフィルターでは、メソッドは指定されたオカレンスに対してのみ呼び出されるため、それは理にかなっています。効率的。それは私がそれらを使用すると、私はあなたが2でシーケンシャル場合によって何を意味するかわからないんだけど、審査の際などをテストする必要があります追加のコードを記述する必要性を減少させることを述べることができることを意味作る
user447607

1. Optionalはの代替ですが、null並列操作とは何の関係もありません。あなたの質問の「今、私はオプションを考えていたことに気付いた」以外は、null取り扱いについて話しているだけですか?
Alexey Romanov

2と3の順序を変更し、両方を少し拡張しました。
Alexey Romanov

6

シーケンスの要素に何らかの関数を適用したいので、シーケンス(配列、コレクション、入力など)をループします。

ストリームを使用すると、シーケンス要素に関数を作成し、具体的なケースとは関係なく、最も一般的な関数(マッピング、フィルタリング、検索、ソート、収集など)を実装できます。

したがって、ほとんどの場合、いくつかのループタスクがあれば、Streamsを使用して少ないコードでそれを表現できます。つまり、読みやすさを得ることができます。


4
まあ、それは読みやすさだけではありません。記述する必要のないコードは、テストする必要のないコードです。
user447607

3
あなたのインタビューにも良い答えを思いついているようです
wero

6

その並列化はとても使いやすいと思います。forループと並行して数百万を超えるエントリを反復してみてください。私たちは多くのCPUに行きますが、速くはありません。ですから、並行して実行する方が簡単であるほど良いのStreamです。

私が好きなのは、彼らが提供する冗長性です。彼らが実際に何をして何を生み出すかを理解するのに彼らがそれをどのように行うではなく、少しの時間しかかかりません。

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