Element finden
Mittlerweile kennen wir diese elegant gestaltete Methode zur Transformation von Sammlungen, sie ist jedoch für die Suche nach Elementen nutzlos. Aber dafür wurde die Filtermethode geboren.
Wir wollen nun die Namen, die mit N beginnen, aus einer Namensliste entfernen. Natürlich kann es keine geben und das Ergebnis kann eine leere Menge sein. Lassen Sie es uns zunächst mit der alten Methode implementieren.
Kopieren Sie den Codecode wie folgt:
final List<String> getsWithN = new ArrayList<String>();
for(String name : friends) {
if(name.startsWith("N")) {
startetWithN.add(name);
}
}
So viel Code für ein so einfaches Ereignis zu schreiben, ist ziemlich ausführlich. Wir erstellen zunächst eine Variable und initialisieren sie dann mit einer leeren Sammlung. Durchlaufen Sie dann die ursprüngliche Sammlung und finden Sie die Namen, die mit dem angegebenen Buchstaben beginnen. Wenn es gefunden wird, wird es in die Sammlung eingefügt.
Lassen Sie uns die Filtermethode verwenden, um den obigen Code zu rekonstruieren, um zu sehen, wie leistungsfähig er ist.
Kopieren Sie den Codecode wie folgt:
final List<String> getsWithN =
friends.stream()
.filter(name -> name.startsWith("N"))
.collect(Collectors.toList());
Die Filtermethode empfängt einen Lambda-Ausdruck, der einen booleschen Wert zurückgibt. Wenn der Ausdruck „true“ ergibt, wird das Element im Ausführungskontext zum Ergebnissatz hinzugefügt; andernfalls wird es übersprungen. Was schließlich zurückgegeben wird, ist ein Steam, der nur die Elemente enthält, deren Ausdruck „true“ zurückgibt. Schließlich verwenden wir eine Collect-Methode, um die Sammlung in eine Liste umzuwandeln – wir werden diese Methode ausführlicher in „Verwenden der Collect-Methode und der Collectors-Klasse“ auf Seite 52 besprechen.
Drucken wir die Elemente in dieser Ergebnismenge aus:
Kopieren Sie den Codecode wie folgt:
System.out.println(String.format("%d Namen gefunden", startetWithN.size()));
Aus der Ausgabe geht hervor, dass diese Methode alle passenden Elemente in der Sammlung gefunden hat.
Kopieren Sie den Codecode wie folgt:
2 Namen gefunden
Die Filtermethode gibt ebenso wie die Map-Methode einen Iterator zurück, aber das ist auch schon alles. Die von Map zurückgegebene Sammlung hat die gleiche Größe wie die Eingabesammlung, es ist jedoch schwer zu sagen, was der Filter zurückgibt. Der Größenbereich des zurückgegebenen Satzes, von 0 bis zur Anzahl der Elemente im Eingabesatz. Im Gegensatz zu Map gibt der Filter eine Teilmenge des Eingabesatzes zurück.
Bisher sind wir mit der Einfachheit des Codes durch Lambda-Ausdrücke sehr zufrieden, aber wenn wir nicht aufpassen, wird das Problem der Coderedundanz langsam zunehmen. Lassen Sie uns dieses Problem weiter unten besprechen.
Wiederverwendung von Lambda-Ausdrücken
Lambda-Ausdrücke sehen sehr prägnant aus, aber tatsächlich kann es leicht passieren, dass Code überflüssig wird, wenn man nicht aufpasst. Redundanz führt zu geringer Codequalität und Schwierigkeiten bei der Wartung. Wenn wir eine Änderung vornehmen möchten, müssen wir mehrere zusammengehörige Codes gemeinsam ändern.
Auch die Vermeidung von Redundanz kann uns dabei helfen, die Leistung zu verbessern. Der relevante Code ist an einem Ort konzentriert, sodass wir seine Leistung analysieren und dann den Code hier optimieren können, wodurch die Leistung des Codes leicht verbessert werden kann.
Schauen wir uns nun an, warum die Verwendung von Lambda-Ausdrücken leicht zu Coderedundanz führen kann, und überlegen wir, wie wir diese vermeiden können.
Kopieren Sie den Codecode wie folgt:
final List<String> friends =
Arrays.asList("Brian", "Nate", "Neal", "Raju", "Sara", "Scott");
final List<String> editors =
Arrays.asList("Brian", "Jackie", "John", "Mike");
final List<String> Genossen =
Arrays.asList("Kate", "Ken", "Nick", "Paula", "Zach");
Wir möchten Namen herausfiltern, die mit einem bestimmten Buchstaben beginnen.
Wir möchten Namen filtern, die mit einem bestimmten Buchstaben beginnen. Lassen Sie es uns zunächst einfach mit der Filtermethode implementieren.
Kopieren Sie den Codecode wie folgt:
final long countFriendsStartN =
friends.stream()
.filter(name -> name.startsWith("N")).count();
final long countEditorsStartN =
editors.stream()
.filter(name -> name.startsWith("N")).count();
final long countComradesStartN =
Genossen.stream()
.filter(name -> name.startsWith("N")).count();
Lambda-Ausdrücke lassen den Code prägnant aussehen, bringen aber unwissentlich Redundanz in den Code. Wenn wir im obigen Beispiel den Lambda-Ausdruck ändern möchten, müssen wir mehr als eine Stelle ändern – was nicht möglich ist. Glücklicherweise können wir Variablen Lambda-Ausdrücke zuweisen und sie genau wie Objekte wiederverwenden.
Die Filtermethode, der Empfänger des Lambda-Ausdrucks, erhält einen Verweis auf die Funktionsschnittstelle java.util.function.Predicate. Auch hier kommt der Java-Compiler zum Einsatz. Er generiert eine Implementierung der Testmethode von Predicate unter Verwendung des angegebenen Lambda-Ausdrucks. Jetzt können wir den Java-Compiler expliziter auffordern, diese Methode zu generieren, anstatt sie dort zu generieren, wo die Parameter definiert sind. Im obigen Beispiel können wir den Lambda-Ausdruck explizit in einer Referenz vom Typ Predicate speichern und diese Referenz dann an die Filtermethode übergeben, wodurch Coderedundanz leicht vermieden werden kann.
Lassen Sie uns den vorherigen Code umgestalten, damit er dem DRY-Prinzip entspricht. (Wiederholen Sie sich nicht – DRY – Prinzip, siehe Buch The Pragmatic Programmer: From Journeyman to Master [HT00]).
Kopieren Sie den Codecode wie folgt:
final Predicate<String> getsWithN = name -> name.startsWith("N");
final long countFriendsStartN =
friends.stream()
.filter(startsWithN)
.zählen();
final long countEditorsStartN =
editors.stream()
.filter(startsWithN)
.zählen();
final long countComradesStartN =
Genossen.stream()
.filter(startsWithN)
.zählen();
Anstatt den Lambda-Ausdruck nun noch einmal zu schreiben, schreiben wir ihn einmal und speichern ihn in einer Referenz vom Typ Predicate mit dem Namen „startsWithN“. In den folgenden drei Filteraufrufen sah der Java-Compiler den als Predicate getarnten Lambda-Ausdruck, lächelte und akzeptierte ihn schweigend.
Diese neu eingeführte Variable beseitigt für uns Code-Redundanz. Aber leider wird der Feind, wie wir später sehen werden, bald zurückkommen, um sich zu rächen. Mal sehen, welche mächtigeren Waffen ihn für uns zerstören können.