JavaのラムダforEach()からの戻り値


95

forEach()ラムダ式の可能性を発見するために、いくつかのfor-eachループをラムダメソッドに変更しようとしています。以下が可能であるようです:

ArrayList<Player> playersOfTeam = new ArrayList<Player>();      
for (Player player : players) {
    if (player.getTeam().equals(teamName)) {
        playersOfTeam.add(player);
    }
}

ラムダ付き forEach()

players.forEach(player->{if (player.getTeam().equals(teamName)) {playersOfTeam.add(player);}});

しかし、次のものは機能しません:

for (Player player : players) {
    if (player.getName().contains(name)) {
        return player;
    }
}

ラムダ付き

players.forEach(player->{if (player.getName().contains(name)) {return player;}});

最後の行の構文に何か問題がありますforEach()か、それともメソッドから戻ることは不可能ですか?


ラムダの内部についてはまだあまり詳しくありませんが、「何から戻るのか」という質問を自分に尋ねると、最初の疑いは、それがメソッドではないことでしょう。
ギンビー2014年

1
@Gimbyはい、returnステートメント内ではラムダはラムダと呼ばれるものからではなく、ラムダ自体から戻ります。Ian Robertsの回答にfindFirst示されているように、ストリームの早期終了(「短絡」)の使用。
スチュアートマーク

回答:


118

returnラムダ式からではなく含む方法からそこに戻っています。forEachあなたがfilterストリームする必要がある代わりに:

players.stream().filter(player -> player.getName().contains(name))
       .findFirst().orElse(null);

ここでfilterは、ストリームを述語に一致するアイテムに制限し、最初に一致するエントリを持つをfindFirst返しますOptional

これはforループアプローチよりも効率が悪いように見えますが、実際にfindFirst()は短絡する可能性があります。フィルタリングされたストリーム全体を生成してから1つの要素を抽出するのではなく、必要な数の要素のみをフィルタリングして最初に一致するものを見つけます。(順序付けられた)ストリームから最初に一致するプレーヤーを取得する必要がない場合findAny()findFirst()、代わりに使用することもできます。これにより、並列処理が含まれる場合に効率が向上します。


ありがとう、それが私が探していたものです!探検するJava8にはたくさんの新しいものがあるようです:)
samutamm

10
合理的ですが、では使用orElse(null)しないことをお勧めしますOptional。の主なポイントはOptional、NPEをオーバーロードする(NPEにつながる)代わりに、値の有無を示す方法を提供することです。あなたがoptional.orElse(null)それを使うなら、ヌルの問題のすべてを買い戻します。呼び出し元を変更できず、実際にはnullが必要な場合にのみ使用します。
スチュアートマーク

1
@StuartMarksは確かに、メソッドの戻り値の型をに変更Optional<Player>することは、ストリームパラダイムに適合するより自然な方法です。ラムダを使用して既存の動作を複製する方法を示すだけでした。
Ian Roberts 2014年

for(Part part:parts)if(!part.isEmpty())がfalseを返す; 何が本当に短いのかしら。そしてより明確に。JavaストリームAPIは、Java言語とJava環境を本当に破壊しました。2020年にJavaプロジェクトで働く悪夢
mmm

15

まず、全体像でJava 8を理解しようとすることをお勧めします。最も重要なのは、ストリーム、ラムダ、メソッド参照です。

あなたがすべき決して行単位でのJava 8コードに既存のコードを変換していない、あなたは、特徴を抽出し、それらを変換する必要があります。

最初のケースで私が特定したのは次のとおりです。

  • 入力構造の要素が何らかの述語と一致する場合、それらを出力リストに追加する必要があります。

それを行う方法を見てみましょう。次のように実行できます。

List<Player> playersOfTeam = players.stream()
    .filter(player -> player.getTeam().equals(teamName))
    .collect(Collectors.toList());

ここで行うことは次のとおりです。

  1. 入力構造をストリームに変換します(ここではタイプCollection<Player>であると想定していますが、これでStream<Player>
  2. 不要な要素をすべてで除外し、すべてのPredicate<Player>プレーヤーを保持したい場合はブール値trueにマッピングします。
  3. 結果の要素をを介してリストに収集しCollectorます。ここでは、標準ライブラリコレクターの1つであるを使用できますCollectors.toList()

これには、他の2つのポイントも組み込まれています。

  1. インターフェースに対してコーディングするので、List<E>オーバーに対してコーディングしArrayList<E>ます。
  2. の型パラメーターにダイヤモンド推論をnew ArrayList<>()使用します。結局、Java 8を使用しています。

次に、2番目のポイントに移ります。

全体像を見ずに、レガシーJavaをJava 8に変換したいとします。この部分はすでに@IanRobertsによって回答されていますが、players.stream().filter(...)...彼が提案したことを行う必要があると思います。


5

ブール値を返したい場合は、次のようなものを使用できます(フィルターよりはるかに高速)。

players.stream().anyMatch(player -> player.getName().contains(name));


1

例外をスローすることもできます:

注意:

読みやすくするために、ストリームの各ステップは新しい行に記載する必要があります。

players.stream()
       .filter(player -> player.getName().contains(name))
       .findFirst()
       .orElseThrow(MyCustomRuntimeException::new);

ロジックが緩やかに「例外駆動型」である場合(コード内にすべての例外をキャッチし、次に何をするかを決定する場所が1つあるなど)。例外駆動型の開発を使用するのは、コードベースを倍数で散らかすことを回避でき、try-catchこれらの例外をスローすることが非常に特殊なケースであり、それらを予期して適切に処理できる場合のみです。)

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