forループの代わりに「機能的操作」を使用する必要があるのはなぜですか?


39
for (Canvas canvas : list) {
}

NetBeansは、「機能的操作」を使用することを提案しています。

list.stream().forEach((canvas) -> {
});

しかし、なぜこれが好ましいのでしょうか?どちらかといえば、読みやすく、理解しにくいです。を呼び出してからstream()forEach()パラメーター付きのラムダ式を使用していますcanvasfor最初のスニペットのループよりも優れていることはわかりません。

明らかに、私は美学だけについて話している。おそらく、ここには技術的な利点がありますが、私には欠けています。それは何ですか?代わりに2番目の方法を使用する必要があるのはなぜですか?



15
特定の例では、優先されません。
ロバートハーヴェイ

1
唯一の操作がforEachである限り、私はあなたに同意する傾向があります。パイプラインに他の操作を追加するか、出力シーケンスを作成するとすぐに、ストリームアプローチが推奨されます。
ジャックB

@RobertHarveyでしょ?何故なの?
サラ

@RobertHarveyよく受け入れられた答えは、より複雑な場合にバージョンが水から吹き飛ばされる方法を実際に示していることに同意しますが、些細な場合の「勝利」の理由はわかりません。あなたはそれが自明であるようにそれを述べるが、私はそれを見ないので、私は尋ねた。
サラ

回答:


45

ストリームは、入ってくるデータのコレクションまたはストリームの上で実行したいさまざまな操作の構成のためのはるかに優れた抽象化を提供します。特に、要素をマップし、それらをフィルタリングおよび変換する必要がある場合。

あなたの例はあまり実用的ではありません。Oracleサイトの次のコードを検討してください。

List<Transaction> groceryTransactions = new Arraylist<>();
for(Transaction t: transactions){
  if(t.getType() == Transaction.GROCERY){
    groceryTransactions.add(t);
  }
}
Collections.sort(groceryTransactions, new Comparator(){
  public int compare(Transaction t1, Transaction t2){
    return t2.getValue().compareTo(t1.getValue());
  }
});
List<Integer> transactionIds = new ArrayList<>();
for(Transaction t: groceryTransactions){
  transactionsIds.add(t.getId());
}

ストリームを使用して記述できます:

List<Integer> transactionsIds = 
    transactions.stream()
                .filter(t -> t.getType() == Transaction.GROCERY)
                .sorted(comparing(Transaction::getValue).reversed())
                .map(Transaction::getId)
                .collect(toList());

2番目のオプションははるかに読みやすくなっています。そのため、ネストされたループまたは部分処理を行うさまざまなループがある場合、Streams / Lambda APIの使用に非常に適しています。


5
マップフュージョンstream.map(f).map(g)stream.map(f.andThen(g))ビルド/リデュースフュージョン(あるメソッドでストリームをビルドし、それを消費する別のメソッドに渡すときに、コンパイラーがストリームを削除できる)やストリームフュージョン(多くのストリーム操作を融合できる)などの最適化手法があります単一の必須ループにまとめられます)、ストリーム操作をより効率的にすることができます。これらはGHC Haskellコンパイラー、および他のHaskellおよび他の関数型言語コンパイラーで実装されており、Scalaの実験的な研究実装があります。
ヨルグWミットタグ

NetBeansが最適なパスであると判断された場合、コンパイラがforループから関数操作への変換を確実に実行できる/すべきであるため、機能対ループを考慮する場合、パフォーマンスはおそらく要因ではありません。
ライアン

2番目の方が読みやすいことに同意しません。何が起こっているのかを理解するにはしばらく時間がかかります。それ以外の場合は表示されないため、2番目の方法を実行することでパフォーマンス上の利点はありますか?
ボク・マクドナウ

@BokMcDonagh、Meの経験では、新しい抽象化に慣れることを気にしなかった開発者にとっては読みにくいです。それが未来であるので、私はより慣れるためにそのようなAPIをもっと使うことを提案します。Javaの世界だけではありません。
luboskrnac

16

機能的なストリーミングAPIを使用するもう1つの利点は、実装の詳細を隠すことです。それは何をすべきかを説明するだけであり、どのようにすべきかは説明しません。この利点は、シングルスレッドから並列コード実行に変更するために行う必要のある変更を見ると明らかになります。ただ、変更.stream()します.parallelStream()


13

どちらかといえば、読みやすく、理解しにくいです。

それは非常に主観的です。2番目のバージョンは読みやすく、理解しやすいと思います。他の言語(Ruby、Smalltalk、Clojure、Io、Ioke、Sephなど)が行う方法と一致し、理解するために必要な概念が少なくなります(他の言語と同じ通常のメソッド呼び出しですが、最初の例は特殊な構文です)。

どちらかといえば、それは親しみの問題です。


6
ええ、それは本当です。しかし、これは私に対する答えというよりもコメントのように見えます。
オメガ

機能的な操作も特殊な構文を使用します。「->」は新しいものです
ライアン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.