Listenkonvertierung
Das Konvertieren einer Sammlung in eine neue Sammlung ist so einfach wie das Durchlaufen derselben. Angenommen, wir möchten die Namen in einer Liste vollständig in Großbuchstaben umwandeln. Werfen wir einen Blick auf einige Implementierungsmethoden.
Eine Zeichenfolge in Java ist unveränderlich und kann daher nicht geändert werden. Wir können neue Zeichenfolgen generieren, um die ursprünglichen Elemente in der Liste zu ersetzen. Wenn Sie dies tun, geht jedoch die ursprüngliche Liste verloren. Es besteht möglicherweise auch ein unveränderliches Problem, z. B. die von Arrays.asList() generierte Liste, sodass eine Änderung der ursprünglichen Liste nicht funktioniert. Ein weiterer Nachteil besteht darin, dass es schwierig ist, parallel zu arbeiten.
Es ist eine gute Wahl, eine neue All-Caps-Liste zu erstellen.
Auf den ersten Blick mag das wie eine schwache Beratung klingen; Leistung ist ein Thema, das uns allen am Herzen liegt. Überraschenderweise ist die funktionale Programmierung oft leistungsfähiger als die imperative Programmierung, wie wir unter Leistungsprobleme auf Seite 153 besprechen.
Beginnen wir damit, diesen Satz zu verwenden, um einen neuen Satz Großbuchstaben zu generieren.
Kopieren Sie den Codecode wie folgt:
final List<String> UppercaseNames = new ArrayList<String>();
for(String name : friends) {
uppercaseNames.add(name.toUpperCase());
}
Im Imperativcode erstellen wir zunächst eine leere Liste, füllen sie dann mit Namen in Großbuchstaben und fügen einen nach dem anderen ein, während wir die ursprüngliche Liste durchlaufen. Um eine funktionsfähige Version zu erreichen, kann unser erster Schritt darin bestehen, die for-Schleife durch den internen Iterator forEach zu ersetzen, der beim Durchlaufen der Liste auf Seite 19 erwähnt wird, wie im folgenden Beispiel gezeigt.
Kopieren Sie den Codecode wie folgt:
final List<String> UppercaseNames = new ArrayList<String>();
friends.forEach(name -> UppercaseNames.add(name.toUpperCase()));
System.out.println(uppercaseNames);
Wir verwenden einen internen Iterator, müssen aber auch eine neue Liste erstellen und Elemente darin einfügen. Wir können uns noch weiter verbessern.
Verwendung von Lambda-Ausdrücken
In der neu eingeführten Stream-Schnittstelle gibt es eine Kartenmethode, die uns helfen kann, Variabilität zu vermeiden und den Code prägnanter aussehen zu lassen. Steam ist ein bisschen wie ein Sammlungsiterator, bietet aber auch flüssige Funktionen. Mithilfe der Methoden dieser Schnittstelle können wir eine Reihe von Aufrufen kombinieren, sodass der Code in der Reihenfolge gelesen wird, in der das Problem beschrieben wird, wodurch er besser lesbar wird.
Die Map-Methode von Steam kann verwendet werden, um eine Eingabesequenz in eine Ausgabesequenz umzuwandeln – was perfekt zu dem passt, was wir tun möchten.
Kopieren Sie den Codecode wie folgt:
friends.stream()
.map(name -> name.toUpperCase())
.forEach(name -> System.out.print(name + " "));
System.out.println();
Alle Sammlungen in JDK8 unterstützen diese Stream-Methode, die die Sammlung in einer Steam-Instanz kapselt. Die Map-Methode ruft den angegebenen Lambda-Ausdruck oder Codeblock für jedes Element im Stream auf. Die Map-Methode unterscheidet sich stark von der forEach-Methode. forEach führt einfach eine angegebene Funktion für die Elemente in der Sammlung aus. Die Map-Methode sammelt die laufenden Ergebnisse des Lambda-Ausdrucks und gibt einen Ergebnissatz zurück. Zum Schluss drucken wir alle Elemente mit der forEach-Methode.
Die Namen in der neuen Sammlung sind alle in Großbuchstaben geschrieben:
Kopieren Sie den Codecode wie folgt:
BRIAN NATE NEAL RAJU SARA SCOTT
Die Kartenmethode eignet sich sehr gut zum Umwandeln einer Eingabesammlung in eine neue Ausgabesammlung. Diese Methode stellt sicher, dass die Eingabe- und Ausgabesequenzen die gleiche Anzahl von Elementen haben. Die Ein- und Ausgabeelemente können jedoch unterschiedlicher Art sein. In diesem Beispiel sind unsere Eingabe und Ausgabe beide Sammlungen von Zeichenfolgen. Wir können einen Code an die Map-Methode übergeben, damit diese beispielsweise die Anzahl der im Namen enthaltenen Zeichen zurückgibt. In diesem Fall ist die Eingabe immer noch eine Folge von Zeichenfolgen, aber die Ausgabe ist eine Folge von Zahlen, wie im Folgenden.
Kopieren Sie den Codecode wie folgt:
friends.stream()
.map(name -> name.length())
.forEach(count -> System.out.print(count + " "));
Das Ergebnis ist die Anzahl der Buchstaben in jedem Namen:
Kopieren Sie den Codecode wie folgt:
5 4 4 4 4 5
Eine spätere Version des Lambda-Ausdrucks wird verwendet, um explizite Änderungsvorgänge zu vermeiden; ein solcher Code ist sehr prägnant. Das Schreiben auf diese Weise erfordert nicht mehr die Initialisierung einer leeren Sammlung und die Garbage-Variable wird in der zugrunde liegenden Implementierung ausgeblendet.
Referenz zur Verwendungsmethode
Wir können auch Methodenreferenzen verwenden, um es etwas prägnanter zu gestalten. Wenn eine Implementierung einer funktionalen Schnittstelle übergeben werden muss, kann der Java-Compiler Lambda-Ausdrücke oder Methodenverweise akzeptieren. Mit dieser Funktion können Sie name -> name.toUpperCase() durch String::toUpperCase ersetzen, etwa so:
Kopieren Sie den Codecode wie folgt:
friends.stream()
.map(String::toUpperCase)
.forEach(name -> System.out.println(name));
Wenn die Parameter an die generierte Methode – die Implementierung der abstrakten Methode der Funktionsschnittstelle – übergeben werden, ruft Java die toUpperCase-Methode des String-Parameters auf. Diese Parameterreferenz ist hier versteckt. In einem einfachen Szenario wie dem oben genannten können wir Methodenreferenzen verwenden, um Lambda-Ausdrücke zu ersetzen. Weitere Informationen finden Sie unter Wann Methodenreferenzen verwendet werden sollten auf Seite 26.
Kopieren Sie den Codecode wie folgt:
Ein Freund fragte:
Wann sollten Sie Methodenreferenzen verwenden?
Beim Programmieren in Java verwenden wir normalerweise viel häufiger Lambda-Ausdrücke als Methodenreferenzen. Dies bedeutet jedoch nicht, dass Methodenreferenzen unwichtig oder nutzlos sind. Wenn Lambda-Ausdrücke sehr kurz sind, sind sie eine gute Alternative zum direkten Aufruf von Instanzmethoden oder statischen Methoden. Mit anderen Worten: Wenn der Lambda-Ausdruck nur Parameter an den Methodenaufruf übergibt, sollten wir stattdessen Methodenreferenzen verwenden.
Ein solcher Lambda-Ausdruck ähnelt in etwa dem, was Tom Smykowski im Film „Working with a Bug“ gesagt hat: Seine Aufgabe besteht darin, „Anforderungen von Kunden an Softwareentwickler weiterzugeben“. Aus diesem Grund nenne ich dieses Muster der Refactoring-Methodenreferenzen das Wurmmuster.
Die Verwendung von Methodenreferenzen ist nicht nur prägnant, sondern kann auch die Bedeutung und Funktion des Methodennamens selbst besser widerspiegeln.
Bei der Verwendung von Methodenreferenzen spielt der Compiler eine Schlüsselrolle. Das Zielobjekt und die Parameter, auf die die Methode verweist, werden aus den in der generierten Methode übergebenen Parametern abgeleitet. Dadurch können Sie mithilfe von Methodenreferenzen prägnanteren Code schreiben als mit Lambda-Ausdrücken. Wenn jedoch die Parameter vor der Übergabe an die Methode oder der Rückgabe des Aufrufergebnisses geändert werden sollen, können wir diese komfortable Schreibmethode nicht verwenden.
Im vorherigen Beispiel bezieht sich die Methodenreferenz auf eine Instanzmethode. Methodenreferenzen können sich auch auf eine statische Methode und eine Methode beziehen, die Parameter akzeptiert. Wir werden später Beispiele dafür sehen.
Lambda-Ausdrücke können uns dabei helfen, Sammlungen zu durchlaufen und Sammlungen zu transformieren. Wie wir weiter unten sehen werden, hilft es uns auch, schnell ein Element aus einer Sammlung auszuwählen.