JavaRush /Java-Blog /Random-DE /Kaffeepause Nr. 177. Eine detaillierte Anleitung zum Java...

Kaffeepause Nr. 177. Eine detaillierte Anleitung zum Java Stream in Java 8

Veröffentlicht in der Gruppe Random-DE
Quelle: Hackernoon Dieser Beitrag bietet ein detailliertes Tutorial zum Arbeiten mit Java Stream sowie Codebeispiele und Erklärungen. Kaffeepause Nr. 177.  Eine detaillierte Anleitung zum Java Stream in Java 8 – 1

Einführung in Java-Threads in Java 8

Java Streams, die als Teil von Java 8 eingeführt wurden, werden für die Arbeit mit Datensammlungen verwendet. Sie stellen selbst keine Datenstruktur dar, können aber zur Eingabe von Informationen aus anderen Datenstrukturen durch Ordnen und Pipelining verwendet werden, um ein Endergebnis zu erzielen. Hinweis: Es ist wichtig, Stream und Thread nicht zu verwechseln, da beide Begriffe im Russischen oft mit der gleichen Übersetzung „Flow“ bezeichnet werden. Stream bezeichnet ein Objekt zum Ausführen von Vorgängen (am häufigsten zum Übertragen oder Speichern von Daten), während Thread (wörtliche Übersetzung – Thread) ein Objekt bezeichnet, das die parallele Ausführung bestimmter Programmcodes mit anderen Codezweigen ermöglicht. Da Stream keine separate Datenstruktur ist, ändert es nie die Datenquelle. Java-Streams verfügen über die folgenden Funktionen:
  1. Java Stream kann mit dem Paket „java.util.stream“ verwendet werden. Es kann mit dem folgenden Code in ein Skript importiert werden:

    import java.util.stream.* ;

    Mit diesem Code können wir auch problemlos mehrere integrierte Funktionen in Java Stream implementieren.

  2. Java Stream kann Eingaben von Datensammlungen wie Sammlungen und Arrays in Java akzeptieren.

  3. Java Stream erfordert keine Änderung der Eingabedatenstruktur.

  4. Java Stream ändert die Quelle nicht. Stattdessen wird die Ausgabe mithilfe geeigneter Pipeline-Methoden generiert.

  5. Java Streams unterliegen Zwischen- und Terminaloperationen, die wir in den folgenden Abschnitten besprechen werden.

  6. In Java Stream werden Zwischenoperationen per Pipeline ausgeführt und in einem Lazy-Evaluierungsformat ausgeführt. Sie enden mit Terminalfunktionen. Dies bildet das Grundformat für die Verwendung von Java Stream.

Im nächsten Abschnitt werden wir uns die verschiedenen Methoden ansehen, die in Java 8 verwendet werden, um bei Bedarf einen Java-Stream zu erstellen.

Erstellen eines Java-Streams in Java 8

Java-Threads können auf verschiedene Arten erstellt werden:

1. Erstellen eines leeren Streams mit der Methode Stream.empty()

Sie können einen leeren Stream zur späteren Verwendung in Ihrem Code erstellen. Wenn Sie die Methode Stream.empty() verwenden , wird ein leerer Stream generiert, der keine Werte enthält. Dieser leere Stream kann nützlich sein, wenn wir zur Laufzeit eine Nullzeigerausnahme überspringen möchten. Dazu können Sie den folgenden Befehl verwenden:
Stream<String> str = Stream.empty();
Die obige Anweisung generiert einen leeren Stream mit dem Namen str ohne darin enthaltene Elemente. Um dies zu überprüfen, überprüfen Sie einfach die Anzahl oder Größe des Streams mithilfe des Begriffs str.count() . Zum Beispiel,
System.out.println(str.count());
Als Ergebnis erhalten wir am Ausgang 0 .

2. Erstellen Sie einen Stream mit der Stream.builder()-Methode mit einer Stream.Builder-Instanz

Wir können Stream Builder auch verwenden , um einen Stream mithilfe des Builder-Entwurfsmusters zu erstellen. Es ist für den schrittweisen Aufbau von Objekten konzipiert. Sehen wir uns an, wie wir mit Stream Builder einen Stream instanziieren können .
Stream.Builder<Integer> numBuilder = Stream.builder();

numBuilder.add(1).add(2).add( 3);

Stream<Integer> numStream = numBuilder.build();
Mit diesem Code können Sie einen Stream namens numStream erstellen, der int- Elemente enthält . Dank der zuerst erstellten Stream.Builder- Instanz namens numBuilder geht alles recht schnell .

3. Erstellen Sie mit der Methode Stream.of() einen Stream mit den angegebenen Werten

Eine andere Möglichkeit, einen Stream zu erstellen, ist die Verwendung der Stream.of() -Methode . Dies ist eine einfache Möglichkeit, einen Stream mit bestimmten Werten zu erstellen. Es deklariert und initialisiert den Thread. Ein Beispiel für die Verwendung der Stream.of()- Methode zum Erstellen eines Streams:
Stream<Integer> numStream = Stream.of(1, 2, 3);
Dieser Code erstellt einen Stream mit int- Elementen , genau wie wir es in der vorherigen Methode mit Stream.Builder getan haben . Hier haben wir mit Stream.of() direkt einen Stream mit vordefinierten Werten [1, 2 und 3] erstellt .

4. Erstellen Sie mit der Methode Arrays.stream() einen Stream aus einem vorhandenen Array

Eine weitere gängige Methode zum Erstellen eines Threads ist die Verwendung von Arrays in Java. Der Stream wird hier aus einem vorhandenen Array mit der Methode Arrays.stream() erstellt . Alle Array-Elemente werden in Stream-Elemente umgewandelt. Hier ist ein gutes Beispiel:
Integer[] arr = {1, 2, 3, 4, 5};

Stream<Integer> numStream = Arrays.stream(arr);
Dieser Code generiert einen numStream , der den Inhalt eines Arrays namens arr enthält, bei dem es sich um ein Integer-Array handelt.

5. Zusammenführen zweier vorhandener Streams mit der Methode Stream.concat()

Eine weitere Methode, die zum Erstellen eines Streams verwendet werden kann, ist die Methode Stream.concat() . Es wird verwendet, um zwei Threads zu einem einzelnen Thread zu kombinieren. Beide Streams werden der Reihe nach kombiniert. Das bedeutet, dass der erste Thread zuerst kommt, gefolgt vom zweiten Thread und so weiter. Ein Beispiel für eine solche Verkettung sieht so aus:
Stream<Integer> numStream1 = Stream.of(1, 2, 3, 4, 5);

Stream<Integer> numStream2 = Stream.of(1, 2, 3);

Stream<Integer> combinedStream = Stream.concat( numStream1, numStream2);
Die obige Anweisung erstellt einen endgültigen Stream mit dem Namen „ combinedStream“ , der nacheinander Elemente des ersten Streams „ numStream1“ und des zweiten Streams „ numStream2“ enthält .

Arten von Operationen mit Java Stream

Wie bereits erwähnt, können Sie mit Java Stream in Java 8 zwei Arten von Operationen ausführen: Zwischen- und Terminaloperationen. Schauen wir uns jeden von ihnen genauer an.

Zwischenoperationen

Zwischenoperationen generieren einen Ausgabestream und werden nur ausgeführt, wenn eine Terminaloperation auftritt. Dies bedeutet, dass Zwischenoperationen verzögert und per Pipeline ausgeführt werden und nur durch eine Terminaloperation abgeschlossen werden können. Etwas später erfahren Sie mehr über Lazy Evaluation und Pipelining. Beispiele für Zwischenoperationen sind die folgenden Methoden: filter() , map() , different() , peek() , sorted() und einige andere.

Terminalbetrieb

Terminaloperationen vervollständigen die Ausführung von Zwischenoperationen und geben außerdem die Endergebnisse des Ausgabestreams zurück. Da Terminaloperationen das Ende der verzögerten Ausführung und des Pipelinings signalisieren, kann dieser Thread nach einer Terminaloperation nicht erneut verwendet werden. Beispiele für Terminaloperationen sind die folgenden Methoden: forEach() , Collect() , count() , Reduce() und so weiter.

Beispiele für Operationen mit Java Stream

Zwischenoperationen

Hier sind einige Beispiele einiger Zwischenoperationen, die auf einen Java-Stream angewendet werden können:

Filter()

Diese Methode wird verwendet, um Elemente aus einem Stream zu filtern, die einem bestimmten Prädikat in Java entsprechen. Diese gefilterten Elemente bilden dann einen neuen Stream. Schauen wir uns zum besseren Verständnis ein Beispiel an.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> even = numStream.filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println(even);
Abschluss:
[98]
Erläuterung: In diesem Beispiel können Sie sehen, dass gerade Elemente (teilbar durch 2) mit der Methode filter() gefiltert und in einer Ganzzahlliste numStream gespeichert werden , deren Inhalt später gedruckt wird. Da 98 die einzige gerade Ganzzahl im Stream ist, wird sie in der Ausgabe ausgegeben.

Karte()

Mit dieser Methode wird ein neuer Stream erstellt, indem zugeordnete Funktionen für Elemente des ursprünglichen Eingabestreams ausgeführt werden. Möglicherweise hat der neue Stream einen anderen Datentyp. Das Beispiel sieht so aus:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> d = numStream.map(n -> n*2) .collect(Collectors.toList()); System.out.println(d);
Abschluss:
[86, 130, 2, 196, 126]
Erläuterung: Hier sehen wir, dass die Methode „map()“ verwendet wird, um einfach jedes Element des Streams „numStream“ zu verdoppeln . Wie Sie der Ausgabe entnehmen können, wurde jedes Element im Stream erfolgreich verdoppelt.

unterscheidbar()

Diese Methode wird verwendet, um nur einzelne Elemente in einem Stream abzurufen, indem Duplikate herausgefiltert werden. Ein Beispiel dafür sieht so aus:
Stream<Integer> numStream = Stream.of(43,65,1,98,63,63,1); List<Integer> numList = numStream.distinct() .collect(Collectors.toList()); System.out.println(numList);
Abschluss:
[43, 65, 1, 98, 63]
Erläuterung: In diesem Fall wird die Methode different() für numStream verwendet . Es ruft alle einzelnen Elemente in numList ab , indem es Duplikate aus dem Stream entfernt. Wie Sie der Ausgabe entnehmen können, gibt es keine Duplikate, im Gegensatz zum Eingabestream, der ursprünglich zwei Duplikate (63 und 1) hatte.

spähen()

Diese Methode wird verwendet, um zwischenzeitliche Änderungen zu verfolgen, bevor eine Terminaloperation ausgeführt wird. Dies bedeutet, dass mit peek() eine Operation für jedes Element eines Streams ausgeführt werden kann, um einen Stream zu erstellen, für den weitere Zwischenoperationen ausgeführt werden können.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> nList = numStream.map(n -> n*10) .peek(n->System.out.println("Mapped: "+ n)) .collect(Collectors.toList()); System.out.println(nList);
Abschluss:
Zugeordnet: 430 Zugeordnet: 650 Zugeordnet: 10 Zugeordnet: 980 Zugeordnet: 630 [430, 650, 10, 980, 630]
Erläuterung: Hier wird die peek()- Methode verwendet, um Zwischenergebnisse zu generieren, während die map()- Methode auf die Elemente des Streams angewendet wird. Hier können wir feststellen, dass bereits vor der Verwendung der Terminaloperation „collect()“ zum Drucken des endgültigen Inhalts der Liste in der Druckanweisung das Ergebnis für jede Stream-Elementzuordnung im Voraus sequentiell gedruckt wird.

sortiert()

Die Methode sorted() wird verwendet, um die Elemente eines Streams zu sortieren. Standardmäßig werden Elemente in aufsteigender Reihenfolge sortiert. Sie können auch eine bestimmte Sortierreihenfolge als Parameter angeben. Eine Beispielimplementierung dieser Methode sieht folgendermaßen aus:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.sorted().forEach(n -> System.out.println(n));
Abschluss:
1 43 63 65 98
Erläuterung: Hier wird die Methode sorted() verwendet, um die Elemente des Streams standardmäßig in aufsteigender Reihenfolge zu sortieren (da keine bestimmte Reihenfolge angegeben ist). Sie können sehen, dass die in der Ausgabe gedruckten Elemente in aufsteigender Reihenfolge angeordnet sind.

Terminalbetrieb

Hier sind einige Beispiele einiger Terminaloperationen, die auf Java-Streams angewendet werden können:

für jede()

Die Methode forEach() wird verwendet, um alle Elemente eines Streams zu durchlaufen und die Funktion für jedes Element einzeln auszuführen. Dies fungiert als Alternative zu Schleifenanweisungen wie for , while und anderen. Beispiel:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.forEach(n -> System.out.println(n));
Abschluss:
43 65 1 98 63
Erläuterung: Hier wird die Methode forEach() verwendet, um jedes Element des Streams einzeln zu drucken.

zählen()

Die Methode count() wird verwendet, um die Gesamtzahl der im Stream vorhandenen Elemente abzurufen. Sie ähnelt der size()- Methode , die häufig zur Bestimmung der Gesamtzahl der Elemente in einer Sammlung verwendet wird. Ein Beispiel für die Verwendung der count() -Methode mit einem Java-Stream ist wie folgt:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); System.out.println(numStream.count());
Abschluss:
5
Erläuterung: Da numStream 5 ganzzahlige Elemente enthält, wird bei Verwendung der Methode count() darauf 5 ausgegeben.

sammeln()

Die Methode „collect()“ wird verwendet, um veränderliche Reduzierungen von Stream-Elementen durchzuführen. Es kann verwendet werden, um Inhalte aus einem Stream zu entfernen, nachdem die Verarbeitung abgeschlossen ist. Es verwendet die Collector- Klasse, um Reduzierungen durchzuführen .
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> odd = numStream.filter(n -> n % 2 == 1) .collect(Collectors.toList()); System.out.println(odd);
Abschluss:
[43, 65, 1, 63]
Erläuterung: In diesem Beispiel werden alle ungeraden Elemente im Stream gefiltert und in einer Liste mit dem Namen odd gesammelt/reduziert . Am Ende wird eine Liste der ungeraden gedruckt.

min() und max()

Die Methode min() kann, wie der Name schon sagt, in einem Stream verwendet werden, um das minimale Element darin zu finden. Ebenso kann die Methode max() verwendet werden, um das maximale Element in einem Stream zu finden. Versuchen wir anhand eines Beispiels zu verstehen, wie sie verwendet werden können:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); int smallest = numStream.min((m, n) -> Integer.compare(m, n)).get(); System.out.println("Smallest element: " + smallest);
numStream = Stream.of(43, 65, 1, 98, 63); int largest = numStream.max((m, n) -> Integer.compare(m, n)).get(); System.out.println("Largest element: " + largest);
Abschluss:
Kleinstes Element: 1 Größtes Element: 98
Erläuterung: In diesem Beispiel haben wir das kleinste Element im numStream mit der Methode min() und das größte Element mit der Methode max() gedruckt . Beachten Sie, dass wir hier vor der Verwendung der max()- Methode erneut Elemente zum numStream hinzugefügt haben . Dies liegt daran, dass min() eine Terminaloperation ist und den Inhalt des ursprünglichen Streams zerstört und nur das Endergebnis zurückgibt (in diesem Fall die „kleinste“ Ganzzahl).

findAny() und findFirst()

findAny() gibt jedes Element des Streams als Optional zurück . Wenn der Stream leer ist, wird auch ein optionaler Wert zurückgegeben , der leer ist. findFirst() gibt das erste Element des Streams als Optional zurück . Wie die Methode findAny() gibt auch die Methode findFirst() einen leeren optionalen Parameter zurück , wenn der entsprechende Stream leer ist. Schauen wir uns das folgende Beispiel basierend auf diesen Methoden an:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); Optional<Integer> opt = numStream.findFirst();System.out.println(opt); numStream = Stream.empty(); opt = numStream.findAny();System.out.println(opt);
Abschluss:
Optional[43] Optional.leer
Erläuterung: Hier gibt die Methode findFirst() im ersten Fall das erste Element des Streams als Optional zurück . Wenn der Thread dann als leerer Thread neu zugewiesen wird, gibt die Methode findAny() ein leeres Optional zurück .

allMatch() , anyMatch() und noneMatch()

Die Methode allMatch() wird verwendet, um zu prüfen, ob alle Elemente in einem Stream mit einem bestimmten Prädikat übereinstimmen, und gibt in diesem Fall den booleschen Wert true zurück, andernfalls wird false zurückgegeben . Wenn der Stream leer ist, wird true zurückgegeben . Mit der Methode anyMatch() wird überprüft, ob eines der Elemente in einem Stream mit einem bestimmten Prädikat übereinstimmt. Wenn ja, gibt es true zurück, andernfalls false . Wenn der Stream leer ist, wird false zurückgegeben . Die Methode noneMatch() gibt true zurück , wenn kein Element im Stream mit dem Prädikat übereinstimmt, andernfalls false . Ein Beispiel zur Veranschaulichung sieht so aus:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); boolean flag = numStream.allMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.anyMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.noneMatch(n -> n==1);System.out.println(flag);
Abschluss:
falsch wahr falsch
Erläuterung: Für einen Stream numStream , der 1 als Element enthält, gibt die Methode allMatch() false zurück , da nicht alle Elemente 1 sind, sondern nur eines davon. Die Methode „anyMatch()“ gibt „true“ zurück , da mindestens eines der Elemente 1 ist. Die Methode „noneMatch()“ gibt „false“ zurück , da 1 tatsächlich als Element in diesem Stream vorhanden ist.

Faule Auswertungen im Java Stream

Die verzögerte Auswertung führt zu Optimierungen bei der Arbeit mit Java Streams in Java 8. Dabei geht es hauptsächlich darum, Zwischenoperationen zu verzögern, bis eine Terminaloperation auftritt. Die verzögerte Auswertung ist dafür verantwortlich, unnötige Ressourcenverschwendung bei Berechnungen zu verhindern, bis das Ergebnis tatsächlich benötigt wird. Der aus Zwischenoperationen resultierende Ausgabestream wird erst generiert, nachdem die Terminaloperation abgeschlossen ist. Die verzögerte Auswertung funktioniert mit allen Zwischenoperationen in Java-Streams. Eine sehr nützliche Verwendung der verzögerten Auswertung ergibt sich bei der Arbeit mit unendlichen Streams. Auf diese Weise wird eine Menge unnötiger Verarbeitung verhindert.

Pipelines im Java Stream

Eine Pipeline in einem Java-Stream besteht aus einem Eingabestream, null oder mehreren nacheinander aufgereihten Zwischenoperationen und schließlich einer Terminaloperation. Zwischenoperationen in Java Streams werden träge ausgeführt. Dies macht Pipeline-Zwischenoperationen unvermeidlich. Mit Pipelines, bei denen es sich im Grunde genommen um in der Reihenfolge zusammengefasste Zwischenoperationen handelt, wird eine verzögerte Ausführung möglich. Pipelines helfen dabei, den Überblick über Zwischenvorgänge zu behalten, die ausgeführt werden müssen, nachdem schließlich ein Terminalvorgang festgestellt wurde.

Abschluss

Fassen wir nun zusammen, was wir heute gelernt haben. In diesem Artikel:
  1. Wir haben einen kurzen Blick darauf geworfen, was Java Streams sind.
  2. Anschließend lernten wir viele verschiedene Techniken zum Erstellen von Java-Threads in Java 8 kennen.
  3. Wir haben zwei Haupttypen von Operationen (Zwischenoperationen und Terminaloperationen) kennengelernt, die auf Java-Streams ausgeführt werden können.
  4. Anschließend haben wir uns mehrere Beispiele für Zwischen- und Terminaloperationen im Detail angesehen.
  5. Am Ende lernten wir mehr über Lazy Evaluation und schließlich über Pipelining in Java-Threads.
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION