Einführung
Lambda-Ausdrücke sind eine wichtige neue Funktion in Java SE 8. Mit Lambda-Ausdrücken können Sie funktionale Schnittstellen durch Ausdrücke ersetzen. Ein Lambda-Ausdruck ist wie eine Methode. Er stellt eine normale Parameterliste und einen Körper (der ein Ausdruck oder ein Codeblock sein kann) bereit, der diese Parameter verwendet.
Lambda-Ausdrücke erweitern auch die Sammlungsbibliothek. Java SE 8 fügt zwei Pakete für Batch-Vorgänge für Sammlungsdaten hinzu: das Paket java.util.function und das Paket java.util.stream. Ein Stream ist wie ein Iterator, verfügt jedoch über viele zusätzliche Funktionen. Insgesamt sind Lambda-Ausdrücke und -Streams die größten Änderungen seit der Hinzufügung von Generika und Annotationen zur Java-Sprache. In diesem Artikel werden wir die Leistungsfähigkeit von Lambda-Ausdrücken und Streams von einfachen bis hin zu komplexen Beispielen sehen.
Umweltvorbereitung
Wenn Java 8 nicht installiert ist, sollten Sie es zuerst installieren, bevor Sie Lambda und Stream verwenden können (der Übersetzer empfiehlt, es zum Testen in einer virtuellen Maschine zu installieren). Tools und IDEs wie NetBeans und IntelliJ IDEA unterstützen Java 8-Funktionen, einschließlich Lambda-Ausdrücke, wiederholbare Annotationen, kompakte Profile und andere Funktionen.
Syntax des Lambda-Ausdrucks
Grundlegende Syntax:
(Parameter) -> Ausdruck
oder
(Parameter) ->{ Anweisungen }
Hier ist ein einfaches Beispiel für einen Java-Lambda-Ausdruck:
Kopieren Sie den Codecode wie folgt:
// 1. Es sind keine Parameter erforderlich, der Rückgabewert ist 5
() -> 5
// 2. Einen Parameter (numerischer Typ) empfangen und das Zweifache seines Werts zurückgeben
x -> 2*x
// 3. Akzeptiere 2 Parameter (Zahlen) und gib ihre Differenz zurück
(x, y) -> xy
// 4. Empfangen Sie 2 Ganzzahlen vom Typ int und geben Sie deren Summe zurück
(int x, int y) -> x + y
// 5. Akzeptieren Sie ein String-Objekt und geben Sie es auf der Konsole aus, ohne einen Wert zurückzugeben (es sieht so aus, als würde es void zurückgeben)
(String s) -> System.out.print(s)
Einfaches Lambda-Beispiel
Nachdem wir nun wissen, was Lambda-Ausdrücke sind, beginnen wir mit einigen grundlegenden Beispielen. In diesem Abschnitt werden wir sehen, wie sich Lambda-Ausdrücke auf die Art und Weise auswirken, wie wir programmieren. Angenommen, es gibt eine Liste von Spielern, dann kann der Programmierer eine for-Anweisung („for-Schleife“) zum Durchlaufen verwenden, die in Java SE 8 in eine andere Form konvertiert werden kann:
Kopieren Sie den Codecode wie folgt:
String[] atp = {"Rafael Nadal", "Novak Djokovic",
„Stanislas Wawrinka“,
„David Ferrer“, „Roger Federer“,
„Andy Murray“, „Tomas Berdych“,
„Juan Martin Del Potro“};
List<String> player = Arrays.asList(atp);
//Vorherige Schleifenmethode
for (Streicherspieler : Spieler) {
System.out.print(player + "; ");
}
//Lambda-Ausdrücke und funktionale Operationen verwenden
player.forEach((player) -> System.out.print(player + "; "));
//Doppelpunkt-Operator in Java 8 verwenden
player.forEach(System.out::println);
Wie Sie sehen, können Lambda-Ausdrücke unseren Code auf eine Zeile reduzieren. Ein weiteres Beispiel sind grafische Benutzeroberflächenprogramme, bei denen anonyme Klassen durch Lambda-Ausdrücke ersetzt werden können. Ebenso kann es bei der Implementierung der Runnable-Schnittstelle auch so verwendet werden:
Kopieren Sie den Codecode wie folgt:
//Anonyme innere Klasse verwenden
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
System.out.println("Hallo Welt!");
}
});
// Oder Lambda-Ausdruck verwenden
btn.setOnAction(event -> System.out.println("Hallo Welt!"));
Das Folgende ist ein Beispiel für die Verwendung von Lambdas zur Implementierung der Runnable-Schnittstelle:
Kopieren Sie den Codecode wie folgt:
// 1.1 Anonyme innere Klassen verwenden
neuer Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hallo Welt!");
}
}).Start();
// 1.2 Lambda-Ausdruck verwenden
new Thread(() -> System.out.println("Hallo Welt!")).start();
// 2.1 Anonyme innere Klassen verwenden
Runnable Race1 = new Runnable() {
@Override
public void run() {
System.out.println("Hallo Welt!");
}
};
// 2.2 Lambda-Ausdruck verwenden
Runnable Race2 = () -> System.out.println("Hallo Welt!");
// Rufen Sie die run-Methode direkt auf (es wird kein neuer Thread geöffnet!)
Race1.run();
Race2.run();
Der Lambda-Ausdruck von Runnable verwendet das Blockformat, um fünf Codezeilen in eine einzeilige Anweisung umzuwandeln. Als nächstes werden wir im nächsten Abschnitt Lambdas verwenden, um die Sammlung zu sortieren.
Sortieren von Sammlungen mit Lambdas
In Java wird die Comparator-Klasse zum Sortieren von Sammlungen verwendet. Im folgenden Beispiel basieren wir auf dem Namen, Nachnamen, der Länge des Namens und dem letzten Buchstaben des Spielers. Wie im vorherigen Beispiel verwenden wir zunächst eine anonyme innere Klasse zum Sortieren und verwenden dann Lambda-Ausdrücke, um unseren Code zu optimieren.
Im ersten Beispiel sortieren wir die Liste nach Namen. Auf die alte Art und Weise würde der Code so aussehen:
Kopieren Sie den Codecode wie folgt:
String[]player = {"Rafael Nadal", "Novak Djokovic",
„Stanislas Wawrinka“, „David Ferrer“,
„Roger Federer“, „Andy Murray“,
„Tomas Berdych“, „Juan Martin Del Potro“,
„Richard Gasquet“, „John Isner“};
// 1.1 Verwenden Sie anonyme innere Klassen, um Spieler nach Namen zu sortieren
Arrays.sort(players, new Comparator<String>() {
@Override
public int vergleichen(String s1, String s2) {
return (s1.compareTo(s2));
}
});
Mithilfe von Lambdas kann die gleiche Funktionalität mit dem folgenden Code erreicht werden:
Kopieren Sie den Codecode wie folgt:
// 1.2 Lambda-Ausdruck verwenden, um Spieler zu sortieren
Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));
Arrays.sort(players, sortByName);
// 1.3 kann auch folgende Form annehmen:
Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2)));
Andere Rankings sind wie folgt. Wie im obigen Beispiel implementiert der Code Comparator über anonyme innere Klassen und einige Lambda-Ausdrücke:
Kopieren Sie den Codecode wie folgt:
// 1.1 Verwenden Sie anonyme innere Klassen, um Spieler nach Nachnamen zu sortieren
Arrays.sort(players, new Comparator<String>() {
@Override
public int vergleichen(String s1, String s2) {
return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" "))));
}
});
// 1.2 Verwenden Sie den Lambda-Ausdruck, um nach Nachnamen zu sortieren
Comparator<String> sortBySurname = (String s1, String s2) ->
( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) );
Arrays.sort(players, sortBySurname);
// 1.3 Oder so, ich frage mich, ob der ursprüngliche Autor einen Fehler gemacht hat, es gibt so viele Klammern ...
Arrays.sort(players, (String s1, String s2) ->
( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) )
);
// 2.1 Verwenden Sie anonyme innere Klassen, um Spieler nach Namenslänge zu sortieren
Arrays.sort(players, new Comparator<String>() {
@Override
public int vergleichen(String s1, String s2) {
return (s1.length() - s2.length());
}
});
// 2.2 Lambda-Ausdruck verwenden, um nach Namenslänge zu sortieren
Comparator<String> sortByNameLenght = (String s1, String s2) -> (s1.length() - s2.length());
Arrays.sort(players, sortByNameLenght);
// 2.3 oder das
Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length()));
// 3.1 Verwenden Sie eine anonyme innere Klasse, um Spieler nach dem letzten Buchstaben zu sortieren
Arrays.sort(players, new Comparator<String>() {
@Override
public int vergleichen(String s1, String s2) {
return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
}
});
// 3.2 Verwenden Sie den Lambda-Ausdruck, um nach dem letzten Buchstaben zu sortieren
Comparator<String> sortByLastLetter =
(String s1, String s2) ->
(s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));
Arrays.sort(players, sortByLastLetter);
// 3.3 oder das
Arrays.sort(players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)));
Das ist alles, einfach und intuitiv. Im nächsten Abschnitt werden wir weitere Funktionen von Lambdas untersuchen und sie mit Streams verwenden.
Verwendung von Lambdas und Streams
Stream ist ein Wrapper für Sammlungen und wird normalerweise zusammen mit Lambda verwendet. Die Verwendung von Lambdas kann viele Vorgänge unterstützen, z. B. Zuordnen, Filtern, Begrenzen, Sortieren, Zählen, Min., Max., Summen, Sammeln usw. In ähnlicher Weise verwenden Streams verzögerte Operationen, sie lesen nicht tatsächlich alle Daten und die Kettensyntax endet, wenn Methoden wie getFirst() angetroffen werden. In den folgenden Beispielen untersuchen wir, was Lambdas und Streams leisten können. Wir haben eine Person-Klasse erstellt und diese Klasse verwendet, um der Liste einige Daten hinzuzufügen, die für weitere Streaming-Vorgänge verwendet werden. Person ist nur eine einfache POJO-Klasse:
Kopieren Sie den Codecode wie folgt:
öffentliche Klasse Person {
privater String Vorname, Nachname, Beruf, Geschlecht;
privates Gehalt, Alter;
öffentliche Person(String Vorname, String Nachname, String Job,
String (Geschlecht, int. Alter, int. Gehalt) {
this.firstName = firstName;
this.lastName = lastName;
this.gender = Geschlecht;
this.age = Alter;
this.job = job;
this.salary = Gehalt;
}
// Getter und Setter
// .
}
Als Nächstes erstellen wir zwei Listen, die beide zum Speichern von Person-Objekten verwendet werden:
Kopieren Sie den Codecode wie folgt:
List<Person> javaProgrammers = new ArrayList<Person>() {
{
add(new Person("Elsdon", "Jaycob", "Java Programmer", "male", 43, 2000));
add(new Person("Tamsen", "Brittany", "Java Programmer", "female", 23, 1500));
add(new Person("Floyd", "Donny", "Java Programmer", "male", 33, 1800));
add(new Person("Sindy", "Jonie", "Java Programmer", "female", 32, 1600));
add(new Person("Vere", "Hervey", "Java Programmer", "male", 22, 1200));
add(new Person("Maude", "Jaimie", "Java Programmer", "female", 27, 1900));
add(new Person("Shawn", "Randall", "Java Programmer", "male", 30, 2300));
add(new Person("Jayden", "Corrina", "Java Programmer", "female", 35, 1700));
add(new Person("Palmer", "Dene", "Java Programmer", "male", 33, 2000));
add(new Person("Addison", "Pam", "Java Programmer", "female", 34, 1300));
}
};
List<Person> phpProgrammers = new ArrayList<Person>() {
{
add(new Person("Jarrod", "Pace", "PHP Programmer", "male", 34, 1550));
add(new Person("Clarette", "Cicely", "PHP Programmer", "female", 23, 1200));
add(new Person("Victor", "Channing", "PHP-Programmierer", "männlich", 32, 1600));
add(new Person("Tori", "Sheryl", "PHP Programmer", "female", 21, 1000));
add(new Person("Osborne", "Shad", "PHP Programmer", "male", 32, 1100));
add(new Person("Rosalind", "Layla", "PHP Programmer", "female", 25, 1300));
add(new Person("Fraser", "Hewie", "PHP-Programmierer", "männlich", 36, 1100));
add(new Person("Quinn", "Tamara", "PHP Programmer", "female", 21, 1000));
add(new Person("Alvin", "Lance", "PHP Programmer", "male", 38, 1600));
add(new Person("Evonne", "Shari", "PHP Programmer", "female", 40, 1800));
}
};
Jetzt verwenden wir die forEach-Methode, um die obige Liste zu iterieren und auszugeben:
Kopieren Sie den Codecode wie folgt:
System.out.println("Namen aller Programmierer:");
javaProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
phpProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
Wir verwenden auch die forEach-Methode, um das Gehalt des Programmierers um 5 % zu erhöhen:
Kopieren Sie den Codecode wie folgt:
System.out.println("Geben Sie Programmierern eine Gehaltserhöhung von 5 %:");
Consumer<Person> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());
javaProgrammers.forEach(giveRaise);
phpProgrammers.forEach(giveRaise);
Eine weitere nützliche Methode ist filter(), mit der wir PHP-Programmierer mit einem Monatsgehalt von mehr als 1400 $ anzeigen können:
Kopieren Sie den Codecode wie folgt:
System.out.println("Hier sind PHP-Programmierer mit einem Monatsgehalt von mehr als 1.400 $:")
phpProgrammers.stream()
.filter((p) -> (p.getSalary() > 1400))
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
Wir können auch Filter definieren und sie dann wiederverwenden, um andere Vorgänge auszuführen:
Kopieren Sie den Codecode wie folgt:
// Filter definieren
Predicate<Person> ageFilter = (p) -> (p.getAge() > 25);
Predicate<Person> salaryFilter = (p) -> (p.getSalary() > 1400);
Predicate<Person> genderFilter = (p) -> ("female".equals(p.getGender()));
System.out.println("Hier sind PHP-Programmiererinnen, die älter als 24 Jahre sind und ein Monatsgehalt von mehr als 1.400 $ haben:");
phpProgrammers.stream()
.filter(ageFilter)
.filter(gehaltFilter)
.filter(genderFilter)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
//Filter wiederverwenden
System.out.println("Java-Programmiererinnen älter als 24 Jahre:");
javaProgrammers.stream()
.filter(ageFilter)
.filter(genderFilter)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
Verwenden Sie die Limit-Methode, um die Anzahl der Ergebnismengen zu begrenzen:
Kopieren Sie den Codecode wie folgt:
System.out.println("Die ersten 3 Java-Programmierer:");
javaProgrammers.stream()
.limit(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
System.out.println("Die drei besten Java-Programmiererinnen:");
javaProgrammers.stream()
.filter(genderFilter)
.limit(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
Wie sieht es mit dem Sortieren aus? Können wir das im Stream erledigen? Im folgenden Beispiel sortieren wir Java-Programmierer nach Namen und Gehalt, fügen sie in eine Liste ein und zeigen dann die Liste an:
Kopieren Sie den Codecode wie folgt:
System.out.println("Nach Namen sortieren und die 5 besten Java-Programmierer anzeigen:");
List<Person> sortiertJavaProgrammers = javaProgrammers
.Strom()
.sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
.limit(5)
.collect(toList());
sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
System.out.println("Java-Programmierer nach Gehalt sortieren:");
sortiertJavaProgrammers = javaProgrammers
.Strom()
.sorted( (p, p2) -> (p.getSalary() - p2.getSalary()) )
.collect( toList() );
sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; %n", p.getFirstName(), p.getLastName()));
Wenn wir nur an den niedrigsten und höchsten Gehältern interessiert sind, sind die Min- und Max-Methoden schneller als die Auswahl des ersten/letzten nach dem Sortieren:
Kopieren Sie den Codecode wie folgt:
System.out.println("Der am schlechtesten bezahlte Java-Programmierer:");
Person pers = javaProgrammers
.Strom()
.min((p1, p2) -> (p1.getSalary() - p2.getSalary()))
.erhalten()
System.out.printf("Name: %s %s; Gehalt: $%,d.", pers.getFirstName(), pers.getLastName(), pers.getSalary())
System.out.println("Java-Programmierer mit dem höchsten Gehalt:");
Person person = javaProgrammers
.Strom()
.max((p, p2) -> (p.getSalary() - p2.getSalary()))
.erhalten()
System.out.printf("Name: %s %s; Gehalt: $%,d.", person.getFirstName(), person.getLastName(), person.getSalary())
Im obigen Beispiel haben wir gesehen, wie die Collect-Methode funktioniert. In Verbindung mit der Map-Methode können wir die Collect-Methode verwenden, um unsere Ergebnismenge in einen String, ein Set oder ein TreeSet zu packen:
Kopieren Sie den Codecode wie folgt:
System.out.println("Verketten Sie die Vornamen von PHP-Programmierern zu einem String:");
String phpDevelopers = phpProgrammers
.Strom()
.map(Person::getFirstName)
.collect(joining(" ; ")); // Kann als Token in weiteren Operationen verwendet werden
System.out.println("Speichern Sie den Vornamen des Java-Programmierers unter Set:");
Set<String> javaDevFirstName = javaProgrammers
.Strom()
.map(Person::getFirstName)
.collect(toSet());
System.out.println("Vornamen von Java-Programmierern in TreeSet speichern:");
TreeSet<String> javaDevLastName = javaProgrammers
.Strom()
.map(Person::getLastName)
.collect(toCollection(TreeSet::new));
Streams können auch parallel sein. Beispiele sind wie folgt:
Kopieren Sie den Codecode wie folgt:
System.out.println("Berechnen Sie das gesamte an Java-Programmierer gezahlte Geld:");
int totalSalary = javaProgrammers
.parallelStream()
.mapToInt(p -> p.getSalary())
.Summe();
Wir können die summaryStatistics-Methode verwenden, um verschiedene zusammenfassende Daten von Elementen im Stream abzurufen. Als nächstes können wir auf diese Methoden wie getMax, getMin, getSum oder getAverage zugreifen:
Kopieren Sie den Codecode wie folgt:
//Anzahl, Min., Max., Summe und Durchschnitt für Zahlen berechnen
List<Integer> zahlen = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
IntSummaryStatistics Statistiken = Zahlen
.Strom()
.mapToInt((x) -> x)
.summaryStatistics();
System.out.println("Die größte Zahl in der Liste: " + stats.getMax());
System.out.println("Die kleinste Zahl in der Liste: " + stats.getMin());
System.out.println("Summe aller Zahlen: " + stats.getSum());
System.out.println("Durchschnitt aller Zahlen: " + stats.getAverage());
OK, das war's, ich hoffe es gefällt euch!
Zusammenfassen
In diesem Artikel haben wir verschiedene Arten der Verwendung von Lambda-Ausdrücken kennengelernt, von einfachen Beispielen bis hin zu komplexeren Beispielen mit Lambdas und Streams. Darüber hinaus haben wir gelernt, wie man Lambda-Ausdrücke und die Comparator-Klasse zum Sortieren von Java-Sammlungen verwendet.
Anmerkung des Übersetzers: Obwohl es sehr fortgeschritten aussieht, ist die Essenz eines Lambda-Ausdrucks nur ein „Syntaxzucker“, der vom Compiler abgeleitet wird und Ihnen hilft, ihn zu konvertieren und in regulären Code zu packen, sodass Sie weniger Code verwenden können, um die gleiche Funktion zu erreichen . . Ich empfehle, es nicht wahllos zu verwenden, da es genau wie der von einigen sehr fortgeschrittenen Hackern geschriebene Code ist. Es ist prägnant, schwer zu verstehen und schwer zu debuggen, und das Wartungspersonal wird Sie schimpfen wollen.