Stream-API in Java
Es gibt verschiedene Möglichkeiten, Daten in Java zu verarbeiten – Schleifen, Methoden und unterschiedliche Algorithmen. Mit Java 8 wurde jedoch ein sehr leistungsfähiges Werkzeug eingeführt – die Stream-API.
Einfach ausgedrückt ist die Stream-API eine Möglichkeit, schnell und unkompliziert mit einem Informationsstrom zu arbeiten. In unserem Fall wird dieser Informationsstrom durch Collections dargestellt. Die Stream-API umfasst einige Konzepte. Hier sind die wichtigsten.
Hauptkonzepte
-
Stream: Repräsentiert eine Sequenz von Datenelementen, die verarbeitet werden können;
-
Intermediate Operations: Operationen, die nach ihrer Ausführung einen neuen Stream erzeugen. Beispiele:
filter,map,distinct,sorted; -
Terminal Operations: Operationen, die die Verarbeitung des Streams abschließen und ein Ergebnis zurückgeben. Beispiele:
collect,forEach,count,reduce; -
Parallele Streams: Ermöglichen die parallele Verarbeitung von Daten. Die Methoden
parallel()undparallelStream()werden verwendet, um parallele Streams zu erzeugen.
Genug Theorie, beginnen wir mit dem Programmieren!
Die Deklaration eines Streams erfolgt durch die Verwendung einer Methode auf der Sammlung, die in einen Stream umgewandelt werden soll:
Main.java
12List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream();
Mit der Methode stream() wurde ein Stream von Strings erzeugt. Um jedoch mit dem Stream zu arbeiten, ist es notwendig, Lambda-Ausdrücke zu verstehen, da die Methoden von Streams hauptsächlich mit diesen arbeiten.
Lambda-Ausdrücke
Lambda-Ausdrücke wurden in Java 8 eingeführt und stellen eine vereinfachte Form zur Erstellung anonymer Funktionen in Java dar. Anonyme Funktionen wurden bisher nicht behandelt, da sie bislang nicht zwingend erforderlich waren. Nun werden wir sie anhand von Lambda-Ausdrücken kennenlernen.
Syntax von Lambda-Ausdrücken:
Die allgemeine Syntax für Lambda-Ausdrücke in Java sieht folgendermaßen aus:
Example.java
123(parameters) -> expression // or (parameters) -> { statements; }
-
Parameter: Dies ist eine Parameterliste, die leer sein oder einen oder mehrere Parameter enthalten kann;
-
Pfeil: Dargestellt durch das Symbol
->, das die Parameter vom Rumpf des Lambda-Ausdrucks trennt; -
Ausdruck oder Anweisungen: Dies ist der Funktionsrumpf, der einen Ausdruck oder einen Block von Anweisungen enthält.
Hier ist ein Beispiel für einen Lambda-Ausdruck, der eine einfache Funktion darstellt, die zwei Zahlen addiert:
Example.java
12345678910// Traditional way MathOperation addition = new MathOperation() { @Override public int operate(int a, int b) { return a + b; } }; // Using a lambda expression MathOperation addition = (int a, int b) -> a + b;
Werfen wir einen genaueren Blick darauf, was im obigen Code genau passiert und wie Lambda-Ausdrücke verwendet werden:
Main.java
1234567891011121314package com.example; // Functional interface with a single abstract method interface MyMathOperation { int operate(int a, int b); } public class Main { public static void main(String[] args) { // Using a lambda expression to implement the interface MyMathOperation addition = (a, b) -> a + b; System.out.println("Sum: " + addition.operate(5, 3)); } }
Im obigen Code:
Eine funktionale Schnittstelle MyMathOperation mit einer einzigen abstrakten Methode operate wurde erstellt.
Eine Lambda-Ausdruck wurde verwendet, um diese Methode zu implementieren, wobei die Addition von zwei Zahlen durchgeführt wird.
Das Ergebnis der Addition wurde ausgegeben.
Es ist verständlich, dass es schwierig nachzuvollziehen sein kann, was in diesem Code aktuell passiert. Kehren wir jedoch zurück zur Stream-API, bei der Lambda-Ausdrücke häufig verwendet werden, und versuchen wir zu verstehen, wie man sie in der Praxis einsetzt.
Wie Sie sich erinnern, haben wir zuvor einen Stream von Strings aus einer Liste von Strings erstellt. Nun verwenden wir Stream-Methoden, um jeden String in diesem Stream in Großbuchstaben umzuwandeln:
Main.java
12345678910111213package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); stream.map(e -> e.toUpperCase()).toList(); } }
Im obigen Code wurde ein Lambda-Ausdruck sowie zwei Methoden verwendet: map() und toList(). Während die Funktion der Methode toList() offensichtlich ist, verändert die Methode map() jedes Element im Stream entsprechend dem übergebenen Lambda-Ausdruck.
Im Folgenden wird näher betrachtet, wie der Lambda-Ausdruck hier funktioniert:
Die Methode map() wendet die Methode toUpperCase() auf jedes Element des Streams an. Das Element dieses Streams wurde als e definiert und mit dem Lambda-Ausdruck wurde das Programm angewiesen, diese Methode auf jedes Element anzuwenden.
Dies ist jedoch noch nicht das Ende, da eine intermediäre Operation angewendet wurde. Das bedeutet, dass Operationen auf dem Stream noch nicht abgeschlossen sind. Um die Arbeit am Stream abzuschließen, muss eine terminale Operation angewendet werden, die die Operationen am Stream beendet und einen bestimmten Wert zurückgibt. Beispielsweise kann die Methode toList() verwendet werden, wodurch der modifizierte Stream in eine Liste umgewandelt wird.
Zum Beispiel:
Main.java
1234567891011121314package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.toUpperCase()).toList(); System.out.println(list); } }
Werfen wir einen genaueren Blick auf mögliche Zwischenoperationen im Stream.
Zwischenoperationen
Die Methode map() – diese Methode ist bereits bekannt; sie führt die durch den Lambda-Ausdruck spezifizierten Operationen auf jedem Element im Stream aus.
Beispielsweise kann die Methode substring() auf jedes Element eines String-Streams angewendet werden:
Main.java
123456789101112131415package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
Die Methode filter() verwendet einen Lambda-Ausdruck mit einer Bedingung, anhand derer der Stream gefiltert wird. Das bedeutet, alle Elemente, die die Bedingung erfüllen, verbleiben im Stream, während Elemente, die die Bedingung nicht erfüllen, aus dem Stream entfernt werden. Der Stream wird so angepasst, dass nur noch die Elemente erhalten bleiben, deren Länge größer als 5 ist:
Main.java
12345678910111213141516package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); stream = stream.filter(e -> e.length() > 5); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
Mit der Methode filter() entfernen wir den String "with" aus dem Stream, da dieses Wort weniger als 5 Zeichen hat.
Mehrfache Zwischenoperationen können auch hintereinander verwendet werden.
Zum Beispiel lässt sich der obige Code etwas vereinfachen:
Main.java
12345678910111213141516package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); List<String> list = strings.stream() .filter(e -> e.length() > 5) .map(e -> e.substring(1, 4)) .toList(); System.out.println("Modified list: " + list); } }
Beim Verketten von mehreren Stream-Methoden wird empfohlen, jede Methode in eine neue Zeile zu schreiben, um die Lesbarkeit des Codes deutlich zu verbessern.
Die Methode flatMap() wandelt jedes Element eines Streams in einen neuen Stream um und kombiniert die Ergebnisse zu einem einzigen Stream. Mit dieser Methode kann der Stream also in mehrere Streams aufgeteilt und anschließend zu einem Stream zusammengeführt werden. Zum Beispiel gibt es eine Liste von Zeichenketten, bei denen jede Zeichenkette mehr als ein Wort enthalten kann, wie etwa eine Liste von Vor- und Nachnamen. Und es ist erforderlich, den ersten Buchstaben jedes dieser Wörter zu kapitalisieren:
Main.java
123456789101112131415161718192021222324package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }
Im obigen Code wurde eine separate private Methode geschrieben, die den ersten Buchstaben eines Wortes kapitalisiert, und diese Methode wurde zusammen mit einem Lambda-Ausdruck in der Methode map() verwendet.
Beachte, dass mit der Methode flatMap jedes Element des Streams mithilfe von Arrays.stream(e.split(" ")) in verschiedene Streams aufgeteilt wird. Da die Methode split() ein Array zurückgibt, muss die Methode Arrays.stream() verwendet werden, um dieses Array in Streams zu teilen.
Anschließend werden alle diese Streams zu einem Stream zusammengeführt, woraufhin die geschriebene Methode verwendet wird. Nun liegen alle Vor- und Nachnamen der Benutzer mit großgeschriebenem Anfangsbuchstaben vor.
Wissen Sie, was interessant wäre?
Wenn diese Vor- und Nachnamen in eine HashMap eingefügt werden, wobei der Schlüssel der Nachname und der Wert der Vorname ist.
Lassen Sie uns dies im Code umsetzen:
Main.java
12345678910111213141516171819202122232425262728293031323334package com.example; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); Map<String, String> usersKeyValue = new HashMap<>(); for (int i = 0; i < list.size() - 1; i+=2) { String name = list.get(i); String surname = list.get(i + 1); usersKeyValue.put(surname, name); } System.out.println("Map with surnames as keys and names as values: " + usersKeyValue); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }
Mit einer einfachen Schleife wurden der Vorname und Nachname in Variablen gespeichert und anschließend in die Map eingefügt. Beachten Sie, wie die Schleife funktioniert. Die Variable i wird in jeder Iteration um 2 erhöht, da der Nachname übersprungen werden muss, sobald er bereits erfasst wurde.
- Die Methode
distinct()entfernt Duplikate aus dem Stream. Dies ist im Allgemeinen nützlich, wenn eindeutige Elemente im Stream benötigt werden oder wenn Duplikate aus einer Liste schnell entfernt werden sollen. Dies kann einfach mit folgender Konstruktion erreicht werden:
list.stream().distinct().toList()
-
Die Methode
sortedsortiert alle Elemente im Stream in natürlicher Reihenfolge, also von der kleinsten zur größten Zahl oder alphabetisch. Dies ist ebenfalls nützlich, wenn ein sortierter Stream benötigt wird oder eine Liste schnell sortiert werden soll; -
Die Methode
skip(n)überspringt die erstennElemente des Streams. Dies ist hilfreich beim Arbeiten mit Textdateien, bei denen die ersten n Zeilen beispielsweise Metadaten oder eine Dateibeschreibung enthalten können. Erwähnenswert ist auch die Methodelimit(n), die die Anzahl der Elemente im Stream begrenzt. Selbst wenn ein Stream mit 1000 Elementen erstellt wird und anschließendlimit(200)verwendet wird, enthält der Stream nur die ersten 200 Elemente.
Main.java
123456789101112package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example = example.stream().skip(3).limit(5).toList(); System.out.println("List: " + example); } }
Dies sind die wichtigsten intermediären Methoden, die benötigt werden. Weitere Methoden können in der Link zur offiziellen Java-Dokumentation nachgelesen werden. Im Folgenden werden die Terminalmethoden behandelt.
Terminalmethoden
-
Die bereits bekannte Terminalmethode ist
toList(). Sie wandelt den Stream in eine Liste um und gibt diese zurück. Das bedeutet, dass dieser Stream mit Methoden direkt einer Liste zugewiesen werden kann. Diese Methode wurde in Java 17 eingeführt und ersetzt die komplexere Konstruktioncollect(Collectors.toList()); -
Die Methode
collect()wandelt den Stream ebenfalls in eine bestimmte Datenstruktur um. Sie verwendet als Parameter eine Methode aus dem InterfaceCollectors. Dieses Interface enthält Methoden wietoList(),toSet()undtoCollection(). Zum Beispiel:
Main.java
123456789101112131415package com.example; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Set<Integer> integerSet = example.stream().collect(Collectors.toSet()); System.out.println("List: " + example); System.out.println("Set: " + integerSet); } }
Die Methode forEach() nimmt einen Lambda-Ausdruck entgegen und führt für jedes Element im Stream eine bestimmte Aktion aus.
Zum Beispiel:
Main.java
1234567891011package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example.stream().forEach(e -> System.out.println(e + 1)); } }
Der Unterschied zwischen dieser Methode und der map-Methode besteht darin, dass diese Methode terminal ist und danach keine weiteren Methoden aufgerufen werden können.
Dies sind alle grundlegenden Methoden zur Arbeit mit Streams. Es handelt sich um ein komplexes Thema, das nicht sofort verstanden werden muss. Allerdings ist es ein Thema, das durch Übung beherrscht wird. In den folgenden Übungskapiteln zu Streams werden Sie zahlreiche Möglichkeiten haben, damit zu arbeiten, da es eine sehr bequeme und praktische Methode zur Verarbeitung von Listen und Datenarrays ist!
1. Was ist der Hauptzweck der Stream-API in Java?
2. Welche der folgenden ist eine terminale Operation in der Stream-API?
3. Was bewirkt die map-Operation in der Stream-API?
4. Worin unterscheidet sich die flatMap-Operation von map in der Stream-API?
5. Was bewirkt die filter-Operation in der Stream-API?
6. Was ist der Zweck der forEach-Operation in der Stream-API?
7. Welche der folgenden ist eine Zwischenoperation in der Stream-API?
8. Wie wird die limit-Operation in der Stream-API verwendet?
Danke für Ihr Feedback!
Fragen Sie AI
Fragen Sie AI
Fragen Sie alles oder probieren Sie eine der vorgeschlagenen Fragen, um unser Gespräch zu beginnen
Can you explain more about how lambda expressions work with streams?
What are some common use cases for the Stream API in real-world Java projects?
Could you provide more examples of intermediate and terminal operations?
Großartig!
Completion Rate verbessert auf 4
Stream-API in Java
Swipe um das Menü anzuzeigen
Es gibt verschiedene Möglichkeiten, Daten in Java zu verarbeiten – Schleifen, Methoden und unterschiedliche Algorithmen. Mit Java 8 wurde jedoch ein sehr leistungsfähiges Werkzeug eingeführt – die Stream-API.
Einfach ausgedrückt ist die Stream-API eine Möglichkeit, schnell und unkompliziert mit einem Informationsstrom zu arbeiten. In unserem Fall wird dieser Informationsstrom durch Collections dargestellt. Die Stream-API umfasst einige Konzepte. Hier sind die wichtigsten.
Hauptkonzepte
-
Stream: Repräsentiert eine Sequenz von Datenelementen, die verarbeitet werden können;
-
Intermediate Operations: Operationen, die nach ihrer Ausführung einen neuen Stream erzeugen. Beispiele:
filter,map,distinct,sorted; -
Terminal Operations: Operationen, die die Verarbeitung des Streams abschließen und ein Ergebnis zurückgeben. Beispiele:
collect,forEach,count,reduce; -
Parallele Streams: Ermöglichen die parallele Verarbeitung von Daten. Die Methoden
parallel()undparallelStream()werden verwendet, um parallele Streams zu erzeugen.
Genug Theorie, beginnen wir mit dem Programmieren!
Die Deklaration eines Streams erfolgt durch die Verwendung einer Methode auf der Sammlung, die in einen Stream umgewandelt werden soll:
Main.java
12List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream();
Mit der Methode stream() wurde ein Stream von Strings erzeugt. Um jedoch mit dem Stream zu arbeiten, ist es notwendig, Lambda-Ausdrücke zu verstehen, da die Methoden von Streams hauptsächlich mit diesen arbeiten.
Lambda-Ausdrücke
Lambda-Ausdrücke wurden in Java 8 eingeführt und stellen eine vereinfachte Form zur Erstellung anonymer Funktionen in Java dar. Anonyme Funktionen wurden bisher nicht behandelt, da sie bislang nicht zwingend erforderlich waren. Nun werden wir sie anhand von Lambda-Ausdrücken kennenlernen.
Syntax von Lambda-Ausdrücken:
Die allgemeine Syntax für Lambda-Ausdrücke in Java sieht folgendermaßen aus:
Example.java
123(parameters) -> expression // or (parameters) -> { statements; }
-
Parameter: Dies ist eine Parameterliste, die leer sein oder einen oder mehrere Parameter enthalten kann;
-
Pfeil: Dargestellt durch das Symbol
->, das die Parameter vom Rumpf des Lambda-Ausdrucks trennt; -
Ausdruck oder Anweisungen: Dies ist der Funktionsrumpf, der einen Ausdruck oder einen Block von Anweisungen enthält.
Hier ist ein Beispiel für einen Lambda-Ausdruck, der eine einfache Funktion darstellt, die zwei Zahlen addiert:
Example.java
12345678910// Traditional way MathOperation addition = new MathOperation() { @Override public int operate(int a, int b) { return a + b; } }; // Using a lambda expression MathOperation addition = (int a, int b) -> a + b;
Werfen wir einen genaueren Blick darauf, was im obigen Code genau passiert und wie Lambda-Ausdrücke verwendet werden:
Main.java
1234567891011121314package com.example; // Functional interface with a single abstract method interface MyMathOperation { int operate(int a, int b); } public class Main { public static void main(String[] args) { // Using a lambda expression to implement the interface MyMathOperation addition = (a, b) -> a + b; System.out.println("Sum: " + addition.operate(5, 3)); } }
Im obigen Code:
Eine funktionale Schnittstelle MyMathOperation mit einer einzigen abstrakten Methode operate wurde erstellt.
Eine Lambda-Ausdruck wurde verwendet, um diese Methode zu implementieren, wobei die Addition von zwei Zahlen durchgeführt wird.
Das Ergebnis der Addition wurde ausgegeben.
Es ist verständlich, dass es schwierig nachzuvollziehen sein kann, was in diesem Code aktuell passiert. Kehren wir jedoch zurück zur Stream-API, bei der Lambda-Ausdrücke häufig verwendet werden, und versuchen wir zu verstehen, wie man sie in der Praxis einsetzt.
Wie Sie sich erinnern, haben wir zuvor einen Stream von Strings aus einer Liste von Strings erstellt. Nun verwenden wir Stream-Methoden, um jeden String in diesem Stream in Großbuchstaben umzuwandeln:
Main.java
12345678910111213package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); stream.map(e -> e.toUpperCase()).toList(); } }
Im obigen Code wurde ein Lambda-Ausdruck sowie zwei Methoden verwendet: map() und toList(). Während die Funktion der Methode toList() offensichtlich ist, verändert die Methode map() jedes Element im Stream entsprechend dem übergebenen Lambda-Ausdruck.
Im Folgenden wird näher betrachtet, wie der Lambda-Ausdruck hier funktioniert:
Die Methode map() wendet die Methode toUpperCase() auf jedes Element des Streams an. Das Element dieses Streams wurde als e definiert und mit dem Lambda-Ausdruck wurde das Programm angewiesen, diese Methode auf jedes Element anzuwenden.
Dies ist jedoch noch nicht das Ende, da eine intermediäre Operation angewendet wurde. Das bedeutet, dass Operationen auf dem Stream noch nicht abgeschlossen sind. Um die Arbeit am Stream abzuschließen, muss eine terminale Operation angewendet werden, die die Operationen am Stream beendet und einen bestimmten Wert zurückgibt. Beispielsweise kann die Methode toList() verwendet werden, wodurch der modifizierte Stream in eine Liste umgewandelt wird.
Zum Beispiel:
Main.java
1234567891011121314package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("a", "b", "c"); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.toUpperCase()).toList(); System.out.println(list); } }
Werfen wir einen genaueren Blick auf mögliche Zwischenoperationen im Stream.
Zwischenoperationen
Die Methode map() – diese Methode ist bereits bekannt; sie führt die durch den Lambda-Ausdruck spezifizierten Operationen auf jedem Element im Stream aus.
Beispielsweise kann die Methode substring() auf jedes Element eines String-Streams angewendet werden:
Main.java
123456789101112131415package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
Die Methode filter() verwendet einen Lambda-Ausdruck mit einer Bedingung, anhand derer der Stream gefiltert wird. Das bedeutet, alle Elemente, die die Bedingung erfüllen, verbleiben im Stream, während Elemente, die die Bedingung nicht erfüllen, aus dem Stream entfernt werden. Der Stream wird so angepasst, dass nur noch die Elemente erhalten bleiben, deren Länge größer als 5 ist:
Main.java
12345678910111213141516package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); Stream<String> stream = strings.stream(); stream = stream.filter(e -> e.length() > 5); List<String> list = stream.map(e -> e.substring(1, 4)).toList(); System.out.println("Modified list: " + list); } }
Mit der Methode filter() entfernen wir den String "with" aus dem Stream, da dieses Wort weniger als 5 Zeichen hat.
Mehrfache Zwischenoperationen können auch hintereinander verwendet werden.
Zum Beispiel lässt sich der obige Code etwas vereinfachen:
Main.java
12345678910111213141516package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> strings = Arrays.asList("Unlock", "Infinity", "with", "Codefinity"); System.out.println("List of strings: " + strings); List<String> list = strings.stream() .filter(e -> e.length() > 5) .map(e -> e.substring(1, 4)) .toList(); System.out.println("Modified list: " + list); } }
Beim Verketten von mehreren Stream-Methoden wird empfohlen, jede Methode in eine neue Zeile zu schreiben, um die Lesbarkeit des Codes deutlich zu verbessern.
Die Methode flatMap() wandelt jedes Element eines Streams in einen neuen Stream um und kombiniert die Ergebnisse zu einem einzigen Stream. Mit dieser Methode kann der Stream also in mehrere Streams aufgeteilt und anschließend zu einem Stream zusammengeführt werden. Zum Beispiel gibt es eine Liste von Zeichenketten, bei denen jede Zeichenkette mehr als ein Wort enthalten kann, wie etwa eine Liste von Vor- und Nachnamen. Und es ist erforderlich, den ersten Buchstaben jedes dieser Wörter zu kapitalisieren:
Main.java
123456789101112131415161718192021222324package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }
Im obigen Code wurde eine separate private Methode geschrieben, die den ersten Buchstaben eines Wortes kapitalisiert, und diese Methode wurde zusammen mit einem Lambda-Ausdruck in der Methode map() verwendet.
Beachte, dass mit der Methode flatMap jedes Element des Streams mithilfe von Arrays.stream(e.split(" ")) in verschiedene Streams aufgeteilt wird. Da die Methode split() ein Array zurückgibt, muss die Methode Arrays.stream() verwendet werden, um dieses Array in Streams zu teilen.
Anschließend werden alle diese Streams zu einem Stream zusammengeführt, woraufhin die geschriebene Methode verwendet wird. Nun liegen alle Vor- und Nachnamen der Benutzer mit großgeschriebenem Anfangsbuchstaben vor.
Wissen Sie, was interessant wäre?
Wenn diese Vor- und Nachnamen in eine HashMap eingefügt werden, wobei der Schlüssel der Nachname und der Wert der Vorname ist.
Lassen Sie uns dies im Code umsetzen:
Main.java
12345678910111213141516171819202122232425262728293031323334package com.example; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; public class Main { public static void main(String[] args) { List<String> users = Arrays.asList("Ethan Johnson", "Olivia smith", "mason davis", "Ava taylor", "logan brown", "Emma Anderson", "jackson miller"); System.out.println("List of users: " + users); List<String> list = users.stream() .flatMap(e -> Arrays.stream(e.split(" "))) .map(e -> capitalizeFirstLetter(e)) .toList(); System.out.println("List with capitalized names and surnames: " + list); Map<String, String> usersKeyValue = new HashMap<>(); for (int i = 0; i < list.size() - 1; i+=2) { String name = list.get(i); String surname = list.get(i + 1); usersKeyValue.put(surname, name); } System.out.println("Map with surnames as keys and names as values: " + usersKeyValue); } private static String capitalizeFirstLetter(String word) { if (word == null || word.isEmpty()) { return word; } return Character.toUpperCase(word.charAt(0)) + word.substring(1); } }
Mit einer einfachen Schleife wurden der Vorname und Nachname in Variablen gespeichert und anschließend in die Map eingefügt. Beachten Sie, wie die Schleife funktioniert. Die Variable i wird in jeder Iteration um 2 erhöht, da der Nachname übersprungen werden muss, sobald er bereits erfasst wurde.
- Die Methode
distinct()entfernt Duplikate aus dem Stream. Dies ist im Allgemeinen nützlich, wenn eindeutige Elemente im Stream benötigt werden oder wenn Duplikate aus einer Liste schnell entfernt werden sollen. Dies kann einfach mit folgender Konstruktion erreicht werden:
list.stream().distinct().toList()
-
Die Methode
sortedsortiert alle Elemente im Stream in natürlicher Reihenfolge, also von der kleinsten zur größten Zahl oder alphabetisch. Dies ist ebenfalls nützlich, wenn ein sortierter Stream benötigt wird oder eine Liste schnell sortiert werden soll; -
Die Methode
skip(n)überspringt die erstennElemente des Streams. Dies ist hilfreich beim Arbeiten mit Textdateien, bei denen die ersten n Zeilen beispielsweise Metadaten oder eine Dateibeschreibung enthalten können. Erwähnenswert ist auch die Methodelimit(n), die die Anzahl der Elemente im Stream begrenzt. Selbst wenn ein Stream mit 1000 Elementen erstellt wird und anschließendlimit(200)verwendet wird, enthält der Stream nur die ersten 200 Elemente.
Main.java
123456789101112package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example = example.stream().skip(3).limit(5).toList(); System.out.println("List: " + example); } }
Dies sind die wichtigsten intermediären Methoden, die benötigt werden. Weitere Methoden können in der Link zur offiziellen Java-Dokumentation nachgelesen werden. Im Folgenden werden die Terminalmethoden behandelt.
Terminalmethoden
-
Die bereits bekannte Terminalmethode ist
toList(). Sie wandelt den Stream in eine Liste um und gibt diese zurück. Das bedeutet, dass dieser Stream mit Methoden direkt einer Liste zugewiesen werden kann. Diese Methode wurde in Java 17 eingeführt und ersetzt die komplexere Konstruktioncollect(Collectors.toList()); -
Die Methode
collect()wandelt den Stream ebenfalls in eine bestimmte Datenstruktur um. Sie verwendet als Parameter eine Methode aus dem InterfaceCollectors. Dieses Interface enthält Methoden wietoList(),toSet()undtoCollection(). Zum Beispiel:
Main.java
123456789101112131415package com.example; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); Set<Integer> integerSet = example.stream().collect(Collectors.toSet()); System.out.println("List: " + example); System.out.println("Set: " + integerSet); } }
Die Methode forEach() nimmt einen Lambda-Ausdruck entgegen und führt für jedes Element im Stream eine bestimmte Aktion aus.
Zum Beispiel:
Main.java
1234567891011package com.example; import java.util.Arrays; import java.util.List; public class Main { public static void main(String[] args) { List<Integer> example = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); example.stream().forEach(e -> System.out.println(e + 1)); } }
Der Unterschied zwischen dieser Methode und der map-Methode besteht darin, dass diese Methode terminal ist und danach keine weiteren Methoden aufgerufen werden können.
Dies sind alle grundlegenden Methoden zur Arbeit mit Streams. Es handelt sich um ein komplexes Thema, das nicht sofort verstanden werden muss. Allerdings ist es ein Thema, das durch Übung beherrscht wird. In den folgenden Übungskapiteln zu Streams werden Sie zahlreiche Möglichkeiten haben, damit zu arbeiten, da es eine sehr bequeme und praktische Methode zur Verarbeitung von Listen und Datenarrays ist!
1. Was ist der Hauptzweck der Stream-API in Java?
2. Welche der folgenden ist eine terminale Operation in der Stream-API?
3. Was bewirkt die map-Operation in der Stream-API?
4. Worin unterscheidet sich die flatMap-Operation von map in der Stream-API?
5. Was bewirkt die filter-Operation in der Stream-API?
6. Was ist der Zweck der forEach-Operation in der Stream-API?
7. Welche der folgenden ist eine Zwischenoperation in der Stream-API?
8. Wie wird die limit-Operation in der Stream-API verwendet?
Danke für Ihr Feedback!