1. Liste der Methoden der Stream
Klasse
Die Klasse wurde erstellt, um die Erstellung von Datenstromketten Stream
zu vereinfachen . Um dies zu erreichen, verfügt die Klasse über Methoden, die neue Objekte zurückgeben .Stream<T>
Stream
Jeder dieser Datenströme führt eine einfache Aktion aus. Wenn Sie sie jedoch zu Ketten kombinieren und interessante Lambda-Funktionen hinzufügen , verfügen Sie über einen leistungsstarken Mechanismus zum Generieren der gewünschten Ausgabe. Bald werden Sie es selbst sehen.
Hier sind die Methoden der Stream
Klasse (nur die grundlegendsten):
Methoden | Beschreibung |
---|---|
|
Erstellt einen Stream aus einer Reihe von Objekten |
|
Erzeugt einen Stream gemäß der angegebenen Regel |
|
Verkettet zwei Streams |
|
Filtert die Daten und gibt nur Daten weiter, die der angegebenen Regel entsprechen |
|
Entfernt Duplikate. Daten, die bereits gefunden wurden, werden nicht weitergegeben |
|
Sortiert die Daten |
|
Führt eine Aktion für jedes Element im Stream aus |
|
Gibt einen Stream zurück, der gekürzt ist, sodass er nicht länger als das angegebene Limit ist |
|
Überspringt die ersten n Elemente |
|
Konvertiert Daten von einem Typ in einen anderen |
|
Konvertiert Daten von einem Typ in einen anderen |
|
Überprüft, ob mindestens ein Element im Stream vorhanden ist, das der angegebenen Regel entspricht |
|
Prüft, ob alle Elemente im Stream der angegebenen Regel entsprechen |
|
Prüft, ob keines der Elemente im Stream mit der angegebenen Regel übereinstimmt |
|
Gibt das erste gefundene Element zurück, das der Regel entspricht |
|
Gibt jedes Element im Stream zurück, das der Regel entspricht |
|
Sucht nach dem minimalen Element im Datenstrom |
|
Gibt das maximale Element im Datenstrom zurück |
|
Gibt die Anzahl der Elemente im Datenstrom zurück |
|
Liest alle Daten aus dem Stream und gibt sie als Sammlung zurück |
2. Zwischen- und Endoperationen der Stream
Klasse
Wie Sie sehen, geben nicht alle Methoden in der obigen Tabelle ein zurück Stream
. Dies hängt damit zusammen, dass die Methoden der Klasse in Zwischenmethoden (auch als nicht-terminal bezeichnet ) und TerminalmethodenStream
unterteilt werden können .
Zwischenmethoden
Zwischenmethoden geben ein Objekt zurück, das die Stream
Schnittstelle implementiert, und können miteinander verkettet werden.
Terminalmethoden
Terminalmethoden geben einen anderen Wert als a zurück Stream
.
Methodenaufrufpipeline
So können Sie eine Stream-Pipeline erstellen, die aus einer beliebigen Anzahl von Zwischenmethoden und einem einzelnen Terminal-Methodenaufruf am Ende besteht. Mit diesem Ansatz können Sie recht komplexe Logik implementieren und gleichzeitig die Lesbarkeit des Codes verbessern.

Die Daten innerhalb eines Datenstroms ändern sich überhaupt nicht. Eine Kette von Zwischenmethoden ist eine raffinierte (deklarative) Möglichkeit, eine Datenverarbeitungspipeline anzugeben, die nach dem Aufruf der Terminalmethode ausgeführt wird.
Mit anderen Worten: Wenn die Terminalmethode nicht aufgerufen wird, werden die Daten im Datenstrom in keiner Weise verarbeitet. Erst nach dem Aufruf der Terminalmethode beginnt die Verarbeitung der Daten gemäß den in der Stream-Pipeline angegebenen Regeln.
stream()
.intemediateOperation1()
.intemediateOperation2()
...
.intemediateOperationN()
.terminalOperation();
Vergleich von Zwischen- und Endmethoden:
dazwischenliegend | Terminal | |
---|---|---|
Rückgabetyp | Stream |
keinStream |
Kann mit mehreren Methoden desselben Typs zu einer Pipeline kombiniert werden | Ja | NEIN |
Anzahl der Methoden in einer einzelnen Pipeline | beliebig | nicht mehr als eins |
Erzeugt das Endergebnis | NEIN | Ja |
Beginnt mit der Verarbeitung der Daten im Stream | NEIN | Ja |
Schauen wir uns ein Beispiel an.
Angenommen, wir haben einen Club für Tierliebhaber. Morgen feiert der Club den Ginger Cat Day. Der Club hat Haustierbesitzer, von denen jeder eine Liste mit Haustieren hat. Sie sind nicht auf Katzen beschränkt.
Aufgabe: Sie müssen alle Namen aller Ingwerkatzen identifizieren, um für sie personalisierte Grußkarten für den morgigen „Berufsurlaub“ zu erstellen. Die Grußkarten sollten nach dem Alter der Katze sortiert sein, vom ältesten zum jüngsten.
Zunächst stellen wir einige Klassen zur Verfügung, die bei der Lösung dieser Aufgabe helfen:
public enum Color {
WHITE,
BLACK,
DARK_GREY,
LIGHT_GREY,
FOXY,
GREEN,
YELLOW,
BLUE,
MAGENTA
}
public abstract class Animal {
private String name;
private Color color;
private int age;
public Animal(String name, Color color, int age) {
this.name = name;
this.color = color;
this.age = age;
}
public String getName() {
return name;
}
public Color getColor() {
return color;
}
public int getAge() {
return age;
}
}
public class Cat extends Animal {
public Cat(String name, Color color, int age) {
super(name, color, age);
}
}
public class Dog extends Animal {
public Dog(String name, Color color, int age) {
super(name, color, age);
}
}
public class Parrot extends Animal {
public Parrot(String name, Color color, int age) {
super(name, color, age);
}
}
public class Pig extends Animal {
public Pig(String name, Color color, int age) {
super(name, color, age);
}
}
public class Snake extends Animal {
public Snake(String name, Color color, int age) {
super(name, color, age);
}
}
public class Owner {
private String name;
private List<Animal> pets = new ArrayList<>();
public Owner(String name) {
this.name = name;
}
public List<Animal> getPets() {
return pets;
}
}
Schauen wir uns nun die Selector
Klasse an, in der die Auswahl nach den angegebenen Kriterien getroffen wird:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Selector {
private static List<Owner> owners;
private static void initData() {
final Owner owner1 = new Owner("Ronan Turner");
owner1.getPets().addAll(List.of(
new Cat("Baron", Color.BLACK, 3),
new Cat("Sultan", Color.DARK_GREY, 4),
new Dog("Elsa", Color.WHITE, 0)
));
final Owner owner2 = new Owner("Scarlet Murray");
owner2.getPets().addAll(List.of(
new Cat("Ginger", Color.FOXY, 7),
new Cat("Oscar", Color.FOXY, 5),
new Parrot("Admiral", Color.BLUE, 3)
));
final Owner owner3 = new Owner("Felicity Mason");
owner3.getPets().addAll(List.of(
new Dog("Arnold", Color.FOXY, 3),
new Pig("Vacuum Cleaner", Color.LIGHT_GREY, 8)
));
final Owner owner4 = new Owner("Mitchell Stone");
owner4.getPets().addAll(List.of(
new Snake("Mr. Boa", Color.DARK_GREY, 2)
));
final Owner owner5 = new Owner("Jonathan Snyder");
owner5.getPets().addAll(List.of(
new Cat("Fisher", Color.BLACK, 16),
new Cat("Zorro", Color.FOXY, 14),
new Cat("Margo", Color.WHITE, 3),
new Cat("Brawler", Color.DARK_GREY, 1)
));
owners = List.of(owner1, owner2, owner3, owner4, owner5);
}
}
Was bleibt, ist, der Methode Code hinzuzufügen main
. Derzeit rufen wir zunächst die initData()
Methode auf, die die Liste der Tierbesitzer im Verein füllt. Dann wählen wir die Namen der rotbraunen Katzen aus, sortiert nach ihrem Alter in absteigender Reihenfolge.
Schauen wir uns zunächst den Code an, der keine Streams verwendet, um diese Aufgabe zu lösen:
public static void main(String[] args) {
initData();
List<String> findNames = new ArrayList<>();
List<Cat> findCats = new ArrayList<>();
for (Owner owner : owners) {
for (Animal pet : owner.getPets()) {
if (Cat.class.equals(pet.getClass()) && Color.FOXY == pet.getColor()) {
findCats.add((Cat) pet);
}
}
}
Collections.sort(findCats, new Comparator<Cat>() {
public int compare(Cat o1, Cat o2) {
return o2.getAge() - o1.getAge();
}
});
for (Cat cat : findCats) {
findNames.add(cat.getName());
}
findNames.forEach(System.out::println);
}
Schauen wir uns nun eine Alternative an:
public static void main(String[] args) {
initData();
final List<String> findNames = owners.stream()
.flatMap(owner -> owner.getPets().stream())
.filter(pet -> Cat.class.equals(pet.getClass()))
.filter(cat -> Color.FOXY == cat.getColor())
.sorted((o1, o2) -> o2.getAge() - o1.getAge())
.map(Animal::getName)
.collect(Collectors.toList());
findNames.forEach(System.out::println);
}
Wie Sie sehen, ist der Code viel kompakter. Darüber hinaus stellt jede Zeile der Stream-Pipeline eine einzelne Aktion dar, sodass sie wie Sätze auf Englisch gelesen werden können:
|
Bewegen Sie sich von a Stream<Owner> nach aStream<Pet> |
|
Behalten Sie nur Katzen im Datenstrom bei |
|
Behalten Sie nur Ingwerkatzen im Datenstrom bei |
|
Sortieren Sie in absteigender Reihenfolge nach Alter |
|
Holen Sie sich die Namen |
|
Tragen Sie das Ergebnis in eine Liste ein |
3. Streams erstellen
Die Stream
Klasse verfügt über drei Methoden, die wir noch nicht behandelt haben. Der Zweck dieser drei Methoden besteht darin, neue Threads zu erstellen.
Stream<T>.of(T obj)
Methode
Die of()
Methode erstellt einen Stream , der aus einem einzelnen Element besteht. Dies ist normalerweise erforderlich, wenn beispielsweise eine Funktion ein Stream<T>
Objekt als Argument akzeptiert, Sie jedoch nur ein T
Objekt haben. of()
Dann können Sie die Methode einfach und unkompliziert verwenden, um einen Stream zu erhalten, der aus einem einzelnen Element besteht .
Beispiel:
Stream<Integer> stream = Stream.of(1);
Stream<T> Stream.of(T obj1, T obj2, T obj3, ...)
Methode
Die of()
Methode erstellt einen Stream , der aus übergebenen Elementen besteht . Es sind beliebig viele Elemente zulässig. Beispiel:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Stream<T> Stream.generate(Supplier<T> obj)
Methode
Mit der generate()
Methode können Sie eine Regel festlegen, die zum Generieren des nächsten Elements des Streams verwendet wird, wenn es angefordert wird. Sie können beispielsweise jedes Mal eine Zufallszahl ausgeben .
Beispiel:
Stream<Double> s = Stream.generate(Math::random);
Stream<T> Stream.concat(Stream<T> a, Stream<T> b)
Methode
Die concat()
Methode verkettet die beiden übergebenen Streams zu einem . Beim Lesen der Daten werden diese zuerst aus dem ersten Stream und dann aus dem zweiten gelesen. Beispiel:
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> stream2 = Stream.of(10, 11, 12, 13, 14);
Stream<Integer> result = Stream.concat(stream1, stream2);
4. Daten filtern
Weitere sechs Methoden erstellen neue Datenströme und ermöglichen es Ihnen, Streams zu Ketten (oder Pipelines) unterschiedlicher Komplexität zu kombinieren.
Stream<T> filter(Predicate<T>)
Methode
Diese Methode gibt einen neuen Datenstrom zurück, der den Quelldatenstrom gemäß der übergebenen Regel filtert . Die Methode muss für ein Objekt aufgerufen werden, dessen Typ ist Stream<T>
.
Sie können die Filterregel mithilfe einer Lambda-Funktion angeben , die der Compiler dann in ein Objekt umwandelt Predicate<T>
.
Beispiele:
Verkettete Streams | Erläuterung |
---|---|
|
Behalten Sie nur Zahlen unter drei bei |
|
Behalten Sie nur Zahlen größer als Null bei |
Stream<T> sorted(Comparator<T>)
Methode
Diese Methode gibt einen neuen Datenstrom zurück, der die Daten im Quellstrom sortiert . Sie übergeben einen Komparator , der die Regeln für den Vergleich zweier Elemente des Datenstroms festlegt.
Stream<T> distinct()
Methode
Diese Methode gibt einen neuen Datenstrom zurück, der nur die eindeutigen Elemente im Quelldatenstrom enthält . Alle doppelten Daten werden verworfen. Beispiel:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.distinct(); // 1, 2, 3, 4, 5
Stream<T> peek(Consumer<T>)
Methode
Diese Methode gibt einen neuen Datenstrom zurück , obwohl die darin enthaltenen Daten mit denen im Quellstrom identisch sind. Wenn jedoch das nächste Element aus dem Stream angefordert wird, wird die Funktion, die Sie an die peek()
Methode übergeben haben, damit aufgerufen.
Wenn Sie die Funktion System.out::println
an die peek()
Methode übergeben, werden alle Objekte angezeigt, wenn sie den Stream durchlaufen.
Stream<T> limit(int n)
Methode
Diese Methode gibt einen neuen Datenstrom zurück, der nur erste n
Elemente im Quelldatenstrom enthält . Alle anderen Daten werden verworfen. Beispiel:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.limit(3); // 1, 2, 3
Stream<T> skip(int n)
Methode
Diese Methode gibt einen neuen Datenstrom zurück , der dieselben Elemente wie der Quellstrom enthält , aber die ersten Elemente überspringt (ignoriert). n
Beispiel:
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 2, 2, 2, 3, 4);
Stream<Integer> stream2 = stream.skip(3); // 4, 5, 2, 2, 2, 3, 4