Rückkehr von Lambda forEach () in Java

95

Ich versuche, einige for-each-Schleifen in Lambda- forEach()Methoden zu ändern, um die Möglichkeiten von Lambda-Ausdrücken zu entdecken. Folgendes scheint möglich zu sein:

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

Mit Lambda forEach()

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

Aber der nächste funktioniert nicht:

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

mit Lambda

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

Stimmt etwas in der Syntax der letzten Zeile nicht oder ist es unmöglich, von der forEach()Methode zurückzukehren?

Samutamm
quelle
Ich bin noch nicht so vertraut mit den Interna von Lambdas, aber wenn ich mir die Frage stelle: "Wovon würden Sie zurückkehren?", Wäre mein anfänglicher Verdacht, dass dies nicht die Methode ist.
Gimby
1
@ Gimby Ja, returninnerhalb einer Aussage kehrt Lambda vom Lambda selbst zurück, nicht von dem, was Lambda genannt wird. Sie beenden einen Stream vorzeitig ("Kurzschluss"), findFirstwie in der Antwort von Ian Roberts gezeigt .
Stuart Marks

Antworten:

118

Das returndort kehrt eher vom Lambda-Ausdruck als von der enthaltenden Methode zurück. Stattdessen müssen forEachSie zum filterStream:

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

Hier filterbeschränkt sich der Stream auf die Elemente, die mit dem Prädikat übereinstimmen, und gibt findFirstdann einen Optionalmit dem ersten übereinstimmenden Eintrag zurück.

Dies sieht weniger effizient aus als der For-Loop-Ansatz, findFirst()kann jedoch einen Kurzschluss verursachen. Es wird nicht der gesamte gefilterte Stream generiert und dann ein Element daraus extrahiert, sondern es werden nur so viele Elemente gefiltert, wie erforderlich sind finde den ersten passenden. Sie können auch verwenden, findAny()anstatt, findFirst()wenn Sie nicht unbedingt den ersten passenden Spieler aus dem (bestellten) Stream erhalten möchten, sondern einfach einen passenden Gegenstand. Dies ermöglicht eine bessere Effizienz bei Parallelität.

Ian Roberts
quelle
Danke, das habe ich gesucht! Es scheint viel Neues in Java8 zu geben :)
Samutamm
10
Vernünftig, aber ich schlage vor, dass Sie nicht orElse(null)auf einem verwenden Optional. Der Hauptpunkt von Optionalbesteht darin, eine Möglichkeit bereitzustellen, das Vorhandensein oder Fehlen eines Werts anzuzeigen, anstatt Null zu überladen (was zu NPEs führt). Wenn Sie es verwenden optional.orElse(null), werden alle Probleme mit Nullen zurückgekauft. Ich würde es nur verwenden, wenn Sie den Anrufer nicht ändern können und er wirklich eine Null erwartet.
Stuart Marks
1
@StuartMarks In der Tat wäre das Ändern des Rückgabetyps der Methode Optional<Player>eine natürlichere Möglichkeit, sich in das Streams-Paradigma einzufügen . Ich habe nur versucht zu zeigen, wie man das vorhandene Verhalten mit Lambdas dupliziert.
Ian Roberts
für (Teil Teil: Teile) if (! part.isEmpty ()) gibt false zurück; Ich frage mich, was wirklich kürzer ist. Und klarer. Die Java-Stream-API hat die Java-Sprache und die Java-Umgebung wirklich zerstört. Albtraum für die Arbeit in einem Java-Projekt im Jahr 2020.
mmm
15

Ich empfehle Ihnen, zunächst zu versuchen, Java 8 im gesamten Bild zu verstehen. In Ihrem Fall handelt es sich vor allem um Streams, Lambdas und Methodenreferenzen.

Sie sollten vorhandenen Code niemals zeilenweise in Java 8-Code konvertieren. Sie sollten Funktionen extrahieren und diese konvertieren.

Was ich in Ihrem ersten Fall identifiziert habe, ist Folgendes:

  • Sie möchten einer Ausgabeliste Elemente einer Eingabestruktur hinzufügen, wenn sie mit einem Prädikat übereinstimmen.

Mal sehen, wie wir das machen, wir können es mit folgendem machen:

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

Was Sie hier tun, ist:

  1. Verwandeln Sie Ihre Eingabestruktur in einen Stream (ich gehe hier davon aus, dass es sich um einen Stream handelt Collection<Player>, jetzt haben Sie einen Stream<Player>.
  2. Filtern Sie alle unerwünschten Elemente mit a heraus Predicate<Player>und ordnen Sie jeden Spieler dem booleschen Wert true zu, wenn er beibehalten werden soll.
  3. Sammeln Sie die resultierenden Elemente in einer Liste über a Collector, hier können wir einen der Standardbibliothekssammler verwenden, nämlich Collectors.toList().

Dies beinhaltet auch zwei weitere Punkte:

  1. Code gegen Schnittstellen, also Code gegen List<E>Over ArrayList<E>.
  2. Verwenden Sie die Diamantinferenz für den Typparameter in new ArrayList<>(), Sie verwenden schließlich Java 8.

Nun zu Ihrem zweiten Punkt:

Sie möchten wieder etwas von altem Java in Java 8 konvertieren, ohne das Gesamtbild zu betrachten. Dieser Teil wurde bereits von @IanRoberts beantwortet , obwohl ich denke, dass Sie players.stream().filter(...)...über das hinausgehen müssen, was er vorgeschlagen hat.

Skiwi
quelle
5

Wenn Sie einen booleschen Wert zurückgeben möchten, können Sie Folgendes verwenden (viel schneller als Filter):

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

Das hat mir geholfen:

List<RepositoryFile> fileList = response.getRepositoryFileList();
RepositoryFile file1 = fileList.stream().filter(f -> f.getName().contains("my-file.txt")).findFirst().orElse(null);

Entnommen aus Java 8 Finden eines bestimmten Elements in einer Liste mit Lambda

user5495300
quelle
1

Sie können auch eine Ausnahme auslösen:

Hinweis:

Aus Gründen der Lesbarkeit sollte jeder Stream-Schritt in einer neuen Zeile aufgeführt werden.

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

Wenn Ihre Logik lose "ausnahmegesteuert" ist, z. B. gibt es eine Stelle in Ihrem Code, die alle Ausnahmen abfängt und entscheidet, was als nächstes zu tun ist. Verwenden Sie eine ausnahmegesteuerte Entwicklung nur, wenn Sie vermeiden können, dass Ihre Codebasis mit Vielfachen try-catchübersät ist und diese Ausnahmen für ganz besondere Fälle ausgelöst werden, in denen Sie sie erwarten und die ordnungsgemäß behandelt werden können.)

Nabster
quelle