Transkripte
1. Einführung: Hallo zusammen,
willkommen in der Klasse Java Eight Streams, eine
praktische Einführung. Mein Name ist Geselle, und in diesem Kurs
werde ich Ihnen
alles über Streams
in ihrer Verwendung erzählen . Dieser Kurs zielt darauf ab, zu dosieren, wenn Sie wenig oder keine
Erfahrung mit Streams haben ist Gaze, um Ihnen einen
Einblick in die Möglichkeit von Streams zu geben . Sie erkennen Situationen
, in denen Streams nützlich sein können. Wir werden tiefer in die Nutzung von
Streams und deren Breite eintauchen. Sie möchten sie in
funktionalen Interfaces
und Lambdas verwenden . Ich werde häufig
verwendete Operationen
für diejenigen erklären , die sich nach einem
praktischeren Ansatz sehnen. Dieser Kurs
bietet auch Übungen , um das
erlernte Wissen in die Praxis umzusetzen. Lass uns anfangen.
2. Stream und Gebrauch Fall: Streams-Nutzung und Anwendungsfall. Wasserströme verwenden
einfach gesagt, ein Strom kann verwendet werden, um eine Sammlung von Gegenständen zu
verarbeiten. In einem späteren Kapitel werden
wir uns eingehender mit einigen
der spezifischen Methoden zur
Verarbeitung einer Sammlung befassen. Aber im Moment stellen Sie sich die
Verarbeitung nur als Filtern, Ändern und Anreichern
der Daten für die Sammlung vor. Beachten Sie jedoch, dass
Produktionen, die als
Input für Streams verwendet werden, von den Streams nicht
wirklich geändert werden. Stattdessen erhalten wir durch die Ausführung
eines Streams separate Ergebnisse, die
von der ursprünglichen Sammlung getrennt sind . Diese
Arbeitsschritte sind
funktionsbeschreibend geschrieben. Das heißt, ein Stream liest sich wie
eine Geschichte,
wenn er richtig gemacht wird, was sie
so unglaublich nützlich macht. Um dies zu demonstrieren. Hier ist ein Beispiel für eine
herkömmliche Methode zum Filtern, Sammeln, Sortieren
und Drucken einiger Daten. Ich verwende dieselbe Logik, die in Streams
geschrieben wurde. Ich bin mir sicher, dass Sie irgendwann beide
Seiten
verstehen können , aber
ich denke, wir sind uns alle einig, dass die Verwendung von Streams es viel,
viel einfacher macht , zu verstehen,
was der Code tut. Da hast du es also.
Streams werden verwendet, um eine Sammlung von Elementen
auf sehr vernünftige Weise zu
verarbeiten. Wie funktionieren Streams? Wie bereits erwähnt, arbeiten Streams
an einer Sammlung von Gegenständen. Sie haben vielleicht bemerkt, dass die Wortsammlung
mit einer Schnittstelle übereinstimmt. Auf Java. Die im Java-Util-Paket gefundene Schnittstelle ist mit der Arbeit
mit Streams verknüpft. den meisten Fällen verwenden
Sie normalerweise eine
Liste oder ein Set, um
einen Stream zu starten , der die
Collection-Schnittstelle erweitert. Es gibt andere Möglichkeiten
, einen Stream zu erstellen. Wir werden in dieser Klasse nicht auf
sie eingehen. Sie finden sie
in den Ressourcen. Mit der Ankunft von Java Eight eine Standardmethode namens wurde der Collection-Schnittstelle
eine Standardmethode namens
stream hinzugefügt. Für diejenigen, die
mit Standardmethoden nicht vertraut sind. Mit
Standardmethoden können Sie den Schnittstellen
Ihrer Bibliotheken
neue Funktionen hinzufügen und
die Binärkompatibilität
mit Code sicherstellen , der für ältere
Versionen der Schnittstellen geschrieben wurde. Dieser Standardmethodenstream gibt eine Instanz
des Streams zurück kostet die zentrale API-Klasse , mit der wir mit ihnen
arbeiten können. Nachdem Sie den Stream erstellt
haben, können Sie nun Operationen
miteinander verketten und den Stream ausführen. Dazu später mehr. Fassen wir zusammen, was
wir bisher gelernt haben. Streams werden verwendet, um
eine Sammlung von Elementen
auf sehr lesbare Weise zu verarbeiten . Streams ändern
diese Quellsammlung nicht, sondern erstellen stattdessen
ein separates Ergebnis von der
ursprünglichen Sammlung
getrennt ist. Die Stream-Schnittstelle ist der zentrale API-Preis für die
Arbeit mit Streams. Alles, was
die Collection-Schnittstelle erweitert
oder implementiert , verfügt über eine Standard-Stream-Methode für Interaktion mit der
zentralen API-Klasse
3. Funktionale Schnittstellen und Lambda: Funktionale Interfaces
und Lambdas. Bevor wir
über Stream-Operationen sprechen können, müssen
wir
über die Voraussetzungen für die Verwendung
von Strips sprechen . Die Lambdas. Lambdas sind im Grunde anonyme
innere Klassen, deren
funktionale Schnittstellen
der Java-Compiler implizit die benötigten Informationen
angibt. Fangen wir von vorne an. Anonyme innere Klassen. Bei der Implementierung einer Schnittstelle werden
Sie
alle ihre Methoden implementieren. Hier ist ein Beispiel
, in dem Sie eine
implementierte Klasse erstellen und instanziieren. Hier haben wir eine Schnittstelle namens my interface
with the print. Eine wichtige Methode wird von meiner Schnittstelle
implementiert. Aber was ist, wenn ich eine etwas
andere Implementierung
der Drucke benötige , eine
wichtige Methode. In diesem Szenario
müsste ich
eine andere Klasse erstellen , die meine Schnittstelle
implementiert. Schon wieder. Stellen Sie sich vor, wenn Sie
zehn verschiedene Varianzen haben, wird
es sehr bald
überladen. Das ist ein Hinweis auf
anonyme innere Klassen. Anonyme innere Klasse ist
eine Erweiterung einer Klasse. Eine Implementierung
einer Schnittstelle ohne Namen, daher anonym. Wir konzentrieren uns auf den
Interface-Teil S. Das ist der wichtige Teil
im Kontext von Streams. Da sie keinen Namen haben, können
wir keine Instanz
der anonymen inneren
Klasse alleine erstellen . Stattdessen müssen Sie die
anonyme innere Klasse
in einem einzigen Ausdruck
deklarieren und instanziieren . Dieser Ausdruck
sieht wie folgt aus, was folgendermaßen übersetzt
wird. Beim Erstellen der anonymen
inneren Klassenversion meiner Schnittstelleninformationen. Wie Sie sehen können, haben wir eine Instanz
meiner Schnittstelle
erstellt , ohne dass
eine Klasse benötigt wird,
die das Interface implementiert. Und wenn ich eine neue
Implementierung meiner Schnittstelle hinzufügen möchte, kann ich das einfach tun.
Da haben wir es. Anonyme innere Klassen
können verwendet werden, um
eine Schnittstelle zu implementieren , ohne dass
eine starre Klasse definiert werden muss. Sie können
sie einfach im laufenden Betrieb erstellen. Funktionale Schnittstellen. funktionale Schnittstelle
ist nur eine Schnittstelle außer dass sie nur
eine einzige Methode enthält , die implementiert werden
muss. Jede Schnittstelle, die diese
Kriterien erfüllt, ist eine
funktionale Schnittstelle. Genau wie unser
Beispiel für mein Interface gerade. Obwohl dies nicht erforderlich ist, können Sie
die
Anmerkung zur funktionalen Schnittstelle hinzufügen , um Ihre Absicht zu verdeutlichen. Als zusätzlichen Bonus gibt der Compiler
eine Ausnahmekompilierungszeit aus,
wenn es sich bei Verwendung dieser Annotation nicht wirklich um eine funktionale Schnittstelle wirklich um eine funktionale Schnittstelle
handelt. Gehen wir die
gängigsten Funktionsschnittstellen durch bei der Arbeit mit Streams
verwendet werden. erste ist der
Lieferant mit zugelassenen bekommt. Es erwartet keine Argumente, gibt
aber etwas zurück. Der Lieferant liefert
Ihnen etwas, normalerweise ein neues,
leeres, sauberes Objekt. Man könnte es als Fabrik sehen. Das nächste ist das
Prädikat. Mit zugelassen. Es erwartet ein Argument, wertet es aus und
gibt einen booleschen Wert zurück. Wie der Name schon sagt, ist es im Grunde ein Test. Erfüllt das Argument t die
angegebenen Kriterien ja oder nein? Verbraucher mit seinen
Methoden
akzeptiert, akzeptiert ein Argument,
macht etwas damit, gibt
aber nichts zurück verbraucht dabei
buchstäblich die
Argumente. Der Fahrradkonsument tut
dasselbe wie der Verbraucher, außer dass er zwei Argumente erwartet. Funktion mit ihren Methoden gelten. Erwartet ein Argument, es
macht etwas damit. Und Rückgaben in Argumenten bilden im Grunde
das Argument im Prozess ab oder reichern es an, das Argument r und t
können dieselbe Klasse sein. Die Funktion by macht
dasselbe wie die Funktion. Excepted erwartet zwei Argumente, T und U machen
etwas mit ihnen. Ich bin Returns sind genau
wie die Funktion. Argumente können bei Bedarf
dieselbe Klasse sein. Und in diesem Sinne kommen wir
zur letzten gemeinsamen
Funktionsschnittstelle. Binärer Operator. Der binäre Operator ist eine Erweiterung
von by function, wobei T ,
U und R
genau dieselbe Klasse sind. Stellen Sie sicher, dass Sie ein
gutes Verständnis
der funktionalen Schnittstellen
im Allgemeinen und der
gängigen Schnittstellen haben der funktionalen Schnittstellen , bevor Sie fortfahren. Es wird Ihnen helfen,
Lambdas und
Stream-Operationen leichter zu verstehen . Lambda-Ausdrücke. Am deutlichsten sind
anonyme innere Klassen funktionaler Schnittstellen, von denen der Java-Compiler implizit die benötigten Informationen
kennt. Wir wissen jetzt, was eine
anonyme innere Klasse ist und wie wir noch alle Methoden
einer Schnittstelle implementieren müssen . Wir stellen fest, dass funktionale Schnittstellen reguläre Schnittstellen
sind, außer dass sie nur
eine einzige Methode haben , die implementiert werden
muss. Da eine funktionale Schnittstelle
nur eine einzige Methode hat, ist
der Compiler in der
Lage, viele Informationen
basierend auf dem Kontext zu verstehen . Sie als Entwickler können diese Informationen
weglassen. Lässt den Compiler also wissen, dass
Sie Dinge auslassen. Die Lambda-Syntaxprobleme. Wir beginnen mit einer unregelmäßigen
anonymen inneren Klasse und wandeln sie Stück für
Stück in einen ausgewachsenen
Lambda-Ausdruck um . Oder verwenden Sie das Prädikat der funktionalen
Schnittstelle, um zu testen, ob eine angegebene Ganzzahl höher als eins
ist. das Gleichheitszeichen kümmerten, ist
uns der Name der
Schnittstelle aufgefallen, die wir implementieren. Der Hauptteil der
anonymen inneren Klasse, die Methodensignatur und der Hauptteil der Methoden, die
wir implementieren. Schauen Sie sich den
Schnittstellennamen und die Methodensignatur genauer an. Siehst du, wie der Compiler auf dieses Wissen schließen
könnte? Beachten Sie, dass sich die
Schnittstellennamen bereits auf der linken Seite
der equal-Anweisungen befinden. Beachten Sie, dass, da
das Prädikat eine funktionale Schnittstelle ist, nur eine einzige
Methode implementiert werden muss. Wir wissen also bereits, welche
Methoden wir implementieren. Beide Arten von Informationen können vom Compiler
abgeleitet werden. Unser erster Schritt besteht darin, dies
mit der zusammengefassten
Syntax, dem Pfeil, zu schreiben . Damit. Wir werden den
Schnittstellennamen und den Methodennamen loswerden. Das sieht schon
viel sauberer aus, oder? Hier sehen wir die Eingabe
der Methoden, die Ganzzahl auf der linken
Seite des Pfeils und den Hauptteil der
Methode auf der rechten Seite. Aber es gibt immer noch
einige Informationen wir in diesem Fall auslassen können. Da es eine
einzige Methode gibt und der Compiler deren Signatur kennt
, kann
der Compiler den
Eingabetyp aus dem Kontext ableiten. Die Klammern um die Eingänge
sind ebenfalls verschwunden. Dies ist nur möglich, weil
es sich um einen einzelnen Parameter handelt. Falls Sie zwei oder mehr haben, sind
die Klammern obligatorisch. Es gibt noch eine Sache, auf die
wir jetzt schließen können. Es hat mit dem Körper
und der Rückgabeerklärung zu tun. Im Fall
einer einzigen Codezeile im Hauptteil
des Verfahrens kann
der Compiler daraus schließen, dass diese
Codezeile tatsächlich die
Rückgabeanweisung des Körpers ist. Mit diesem letzten Schritt haben
wir den ausgewachsenen
Lambda-Ausdruck erreicht. Sie werden die ganze Zeit sehen,
wenn Sie mit Strings arbeiten. Eigentlich gibt es eine Möglichkeit, das Lambda noch kürzer zu
schreiben. In einigen Fällen. Dies wird als Methodenreferenz bezeichnet. Aber ich schlage vor,
es selbst nachzuschlagen, wenn Sie ein gutes Verständnis für
lange Tuns im Allgemeinen
haben. Eine letzte wichtige Sache, die bei Lumped Us zu erwähnen ist, ist, dass die in ihnen verwendeten
lokalen Variablen endgültig oder
effektiv endgültig sein
sollten. Tatsächlich
bedeutet final eine Variable , die nicht
das endgültige Schlüsselwort hat, aber ihr Wert ändert sich nicht,
nachdem sie zum ersten Mal zugewiesen wurde. Der Grund dafür ist, dass es sich bei
Lumped Us im Grunde genommen um
einen Code handelt , den Sie mit anderen Methoden
ausführen können. wie Sie unser Prädikat
an eine Filteroperation
innerhalb einer Zeichenfolge
übergeben können . Aus diesem Grund muss
Java
eine Kopie der lokalen Variablen
erstellen , die in der Lunge verwendet werden soll. Um Parallelitätsprobleme zu vermeiden. Das Alpha hat beschlossen, lokale
Variablen vollständig
einzuschränken. Fassen wir zusammen, was
wir bisher gelernt haben. Anonyme innere Klassen
sind innere Klassen , die eine Schnittstelle implementieren,
ohne einen Namen zu haben, diese einmal deklariert
und instanziiert werden müssen. Funktionale Schnittstellen
sind regelmäßig Schnittstellen außer dass sie nur
eine einzige Methode enthalten , die implementiert werden
muss. Die Annotation der funktionalen
Schnittstelle kann verwendet werden, um
diese Regel durchzusetzen, ist nicht erforderlich. Lumped us sind anonyme
innere Klassen funktionaler
Schnittstellen, von denen der Java-Compiler
anhand des Kontextes auf
fehlende Informationen schließen kann . Wir wissen jetzt, wie man
einen Lambda-Ausdruck und eine
unregelmäßige anonyme innere Klasse erzeugt einen Lambda-Ausdruck und . Und schließlich müssen lokale Variablen, die in Lumped Us
verwendet werden,
endgültig oder effektiv in Ordnung sein.
4. Stream: In den nächsten Kapiteln werde
ich häufig verwendete
Operationen an Strings erläutern. Aber zuerst sollten wir über
die beiden Arten
möglicher Operationen sprechen , Zwischen- und
Terminaloperationen. Ein Stream wird nur vollständig ausgeführt, wenn eine
Terminaloperation verwendet wird. Daher endet ein Stream
immer in einem Terminalbetrieb und kann nur einen
davon in einem Stream
haben. Zwischenoperationen
sind alle Operationen bevor wir den endgültigen
Terminalbetrieb erreichen. Zwischenoperationen
geben eine Instanz von Stream zurück, wodurch es möglich ist, sie alle miteinander zu
verketten. Da alle
Zwischenoperationen stream zurückgeben, können
Sie eine Zeichenfolge ohne
Terminaloperationen
in einer Variablen speichern . Vielen Dank noch heute, damit Sie
Ihrem Stream
tatsächlich Schritte hinzufügen können , die auf
externen Einflüssen basieren, z. B. einem anderen Filter oder einer anderen Art der
Datenanreicherung. Achten Sie jedoch darauf,
obwohl Sie
Zwischenoperationen in Variablen
speichern können , ist
es nicht möglich,
denselben Stream zweimal mit
einer Terminaloperation auszuführen . Der Compiler
erkennt dies nicht, aber Sie erhalten während der Laufzeit eine Ausnahme
wie diese. Noch einmal. Zwischenoperationen
geben einen Stream zurück und können daher verknüpft
und in Variablen gespeichert werden. Zwischenoperationen allein führen
keinen Stream aus. Eine Terminaloperation führt den vollständigen Stream aus und gibt
einen anderen Wert als einen Stream zurück. Ein Stream kann nur einmal mit einer
Terminaloperation
ausgeführt werden . zweimaliger Versuch führt
während der Laufzeit zu einer Ausnahme. Mit diesem Wissen gehen
wir zu
den häufig verwendeten Operationen über.
5. Intermediate Operation: Filter: Fangen wir mit den Grundlagen an. Filtern. Filter ist eine
Zwischenoperation, mit der Sie Objekte aus
Strings
herausfiltern können Objekte aus
Strings
herausfiltern , die den angegebenen Test nicht
bestehen. Sie könnten den
Zwischenoperationsfilter als das Stream-Äquivalent
einer if-Anweisung sehen. Eingabe ist ein Prädikat das, wie wir gelernt haben, ein Objekt
empfängt, einen Test darauf
durchführt und wahr oder falsch
zurückgibt, wahr oder falsch
zurückgibt je nachdem, ob es den Test
bestanden hat oder nicht. Hier ist ein Beispiel für seine Verwendung. Und mach dir keine Sorgen um
die anderen Operationen. Wir werden bald zu denen kommen. In diesem Beispiel
verwenden wir Filter, um
den Stream nur mit Objekten fortzusetzen den Stream nur mit Objekten , deren Liste der
Telefonnummern leer ist. Bevor Sie zu den verbleibenden
Kunden als Liste zurückkehren, können Sie anhand eines
beliebigen endgültigen Werts
filtern. Hier filtern wir basierend
auf unserer lokalen Variablen, z. B. können
wir sie
auch mit mehreren
Filtern hintereinander verketten . Die Filteroperation ist ein Paradebeispiel dafür, wie Streams Ihren
Code lesbarer machen. Vor allem, wenn Sie
lange Zeit in einem solchen Wert gespeichert haben. Sie können jetzt auf einen Blick sehen, wonach wir
filtern.
6. Terminal fürJede: Unser erster
Terminalbetrieb für jeden. Das forEach ist eine sehr
einfache Terminalbedienung. Es wird verwendet, um
eine bestimmte Aktion für alle
Elemente im Stream auszuführen . Unter der Annahme, dass die Elemente
im Prozess, seine Eingaben, ein Verbraucher sind. Es nimmt also ein Objekt, macht etwas damit, gibt
aber keinen Wert zurück. Danach. Wir haben
seine Implementierung in der zuvor
diskutierten Operation gesehen, aber hier ist sie noch einmal. In diesem Beispiel drucken wir den Vornamen jeder
Kundenbranche. Denken Sie daran, dass für
jedes Element nur dann gearbeitet wird, wenn sie den
Rest des Streams
durchlaufen haben . Wenn wir also vor
dem Terminalbetrieb einen Filter setzen ,
erhalten nur Kunden
mit weniger als drei Geräten ihren Vornamen, drucken ihn
aus mit weniger als drei Geräten . Wusste sie schließlich, dass normale Methoden auch in einem Klumpen
angewendet werden können? Nehmen Sie diese Methode, z. B. sieht das für Sie nicht wie eine
Verbraucherimplementierung aus? Es akzeptiert ein einzelnes Objekt, macht etwas damit, gibt
aber danach
keinen Wert zurück. Und in der Tat können wir diese
Methode in unserem forEach verwenden, um sie
dabei lesbarer zu machen. Und das war's. Für jeden führt einfach
eine angegebene Aktion für jedes Element des Streams , das
den Rest des Streams durchläuft.
7. Zwischenbetrieb: Karte: Eine weitere grundlegende
Zwischenoperation, Map. Map ist eine
Zwischenoperation, die häufig verwendet wird, um von einem Objekttyp
auf einen anderen zuzuordnen. Es wird jedoch in der Regel auch
zur Ausführung von Geschäftslogik verwendet. Seine Eingaben sind eine Funktion,
die, wie wir jetzt wissen, ein Objekt der Klasse a nimmt, etwas damit
macht und ein Objekt der
Klasse b zurückgibt , wobei Klausel a und
B dieselbe Klasse sein können. Hier ist ein Beispiel, in dem
wir von Klasse A nach B
abbilden. Hier haben wir ein
Kundenobjekt als Eingabe. Wir ordnen das einer
Zeichenfolge zu, die aus
seinem Vor- und
Nachnamen besteht , bevor wir es ausdrucken. In diesem Fall ordnen wir eine Zuordnung
von Klasse A zu Klasse B, von Kunde zu String. In diesem nächsten Beispiel haben
wir ein Kundenobjekt als Eingabe bei der Arbeit mobil
auf ihre Geräte. Geben Sie dann dasselbe
Kundenobjekt zurück und bereichern
Sie Ihr Objekt
effektiv. In diesem Fall
kartieren wir von Kunde zu
Kunde und erreichen das
Objekt auf dem Weg dorthin. Beachten Sie, dass
ich, da der
Lambda-Text zwei Anweisungen enthält, die Klammern hinzufügen
und das Schlüsselwort zurückgeben musste. Schließlich ist
es, genau wie bei jeder
Zwischenoperation, möglich,
mehrere mathematische Operationen
innerhalb desselben Streams zu haben .
8. Zwischenbetrieb: flatMap: Als nächstes FlatMap. Wie der Name schon sagt, ähnelt
FlatMap Map. Es handelt sich um eine
Zwischenoperation, die üblicherweise verwendet wird, um von einem Objekttyp
zu einem anderen zuzuordnen. Mit dem Unterschied, dass FlatMap mit
Eins-zu-Viele-Beziehungen arbeitet. Eingabe ist ebenfalls eine Funktion, aber dem Rückgabeobjekt wird eine Einschränkung
auferlegt. Ein Stream sollte zurückgegeben werden. Hier sehen wir die
Methodensignatur von map und flatMap nebeneinander, um Ihnen den Unterschied zu
zeigen. Während beide die Funktion erwarten, erwartet
FlatMap, dass der r-Wert
vom Typ string ist. Schauen wir uns an, was es macht und wie es
sich davon unterscheidet. In unseren Beispielen haben wir das Kundenobjekt
verwendet, das eine Liste von Geräten enthält. Was würde Ihrer Meinung nach
passieren, wenn wir
die Kartenoperation verwenden würden , um jeden Kunden
auf seine Geräte
umzustellen? Wie Sie in der Ausgabe sehen können, Zuordnung von einem Kunden
zu einer Geräteliste führt die
Zuordnung von einem Kunden
zu einer Geräteliste zu einem Stream
von Gerätelisten. Beim Ausdrucken
werden diese Listen nacheinander gedruckt. Dies könnte etwas sein, das Sie
für Ihren funktionalen Anwendungsfall benötigen . den meisten Fällen interessieren
Sie sich jedoch In den meisten Fällen interessieren
Sie sich jedoch für
alle separaten Geräte nicht für separate
Gerätelisten. Hier kommt FlatMap ins Spiel. Wie schon gesagt. Flatmap legt eine Einschränkung das Rückgabeobjekt
der Funktion fest. Sie können sehen, dass sich das Lambda für
FlatMap leicht geändert
hat und jetzt
das Gerät als Stream zurückgibt. Statt als Liste. Beachten Sie in den Ausgängen
, dass die Geräte jetzt einzeln
gedruckt werden ,
anstatt Liste für Liste. Der Schlüssel dabei ist, dass
FlatMap Streams als
Ergebnisse erwartet , die dann wieder zu einem
einzigen Stream
zusammengeflacht werden. Noch einmal zusammen. Sie können jetzt deutlich den Unterschied
zwischen der Map- und
FlatMap-Operation
erkennen . Zusammenfassend
sollten Maps verwendet werden, wenn Objekte
mit einer Eins-zu-Eins-Beziehung konvertiert werden. Dabei
sollte FlatMap beim
Konvertieren von Objekten mit
einer Eins-zu-Viele-Beziehung verwendet werden. Bei Verwendung von FlatMap ist
der Rückgabewert
ein Stream von Objekten.
9. Zwischenoperationen: sequentiell und parallel: Zwei Operationen derselben
Münze, sequentiell und parallel. Sequentiell und parallel sind
beide Zwischenoperationen , die den gesamten Stream
sequentiell
bzw. parallel machen . Da es sich um
Zwischenoperationen handelt, können
sie
zusammen sogar mehrmals zum Stream hinzugefügt werden . Es spielt keine Rolle, wo
im Stream die
Operation platziert wird. Der gesamte Stream ist entweder
sequentiell oder parallel. Nicht ein bisschen von beidem. Denken Sie daran, dass die letzte sequentielle
oder parallele Operation Industrie
erwähnte, die Industrie
erwähnte, diejenige, die tatsächlich verwendet wird. Das bedeutet, dass dieser Stream genau der gleiche
ist
wie dieser Stream. Ein Stream ist
standardmäßig sequenziell,
was bedeutet, dass die Elemente in der Reihenfolge ausgeführt
werden, in der sie
in der US-Sammlung erscheinen. Das bedeutet auch, dass
diese Streams
die gleiche Ausgabe haben . Wie du siehst. Dies führt dazu,
dass eins bis zehn in der
Reihenfolge mit oder ohne
sequentielle Operation ausgedruckt werden. Wenn wir das
jedoch auf parallel ändern, erhalten
wir, wie Sie sehen können, jedes Mal, wenn wir den Stream ausführen, ein anderes Ergebnis. Der Stream wird
mit mehreren Threads ausgeführt,
was bedeutet, dass die geleistete Arbeit auf
verschiedene Ausführungen
aufgeteilt wird . Die sogenannten Bedrohungen. Beachten Sie, dass das Parallellaufen
eines Streams nicht automatisch
bedeutet, dass der Stream threadsicher ausgeführt werden
kann. Sie als Entwickler
müssen sicherstellen, dass Ihr
Code genauso funktioniert, 1236 oder welche Bedrohungen auch immer. Da will es nicht. Eine weitere Sache, die Sie beim
parallelen Ausführen von Dingen
beachten sollten , ist, dass dies nicht unbedingt bedeutet, dass die
Arbeit schneller erledigt wird. etwas
mit mehreren Threads erstellen , müssen
Sie
die Arbeit in Teile aufteilen für jedes Teil eine Ausführung
erstellen. Delegierte Arbeit, die
verschiedenen Ergebnisse zu einem zusammenführen. Dies führt in unserem Fall zu einem
gewissen Mehraufwand beim Ausführen des Codes oder
Streams, was nur dann von Vorteil wenn Sie genügend Elemente in
Ihrem Stream haben oder wenn Ihr Stream sehr rechenintensiv
ist. Zusammenfassend lässt sich sagen, dass Sie mit den sequentiellen
und parallelen Operationen den gesamten Stream schnell
sequentiell oder parallel gestalten
können . einen Stream parallel ausführen, müssen Sie als Entwickler
sicherstellen, dass der Code threadsicher
ist.
10. Zwischenbetrieb: Blick auf die: Gehen wir mit einem
einfachen, bevor wir zu den komplexen übergehen. Pq. Pq ist eine
Zwischenoperation , mit der
Sie beim
Ausführen einer Zeichenfolge buchstäblich einen Blick
hinter die Kulissen werfen können . Seine Hauptanwendung ist das Debuggen
und Protokollieren seiner Eingaben. Als Verbraucher
bedeutet dies, dass es
einen Wert erhält und etwas
damit macht , ohne
dass es einen Rückgabewert gibt. Während es möglich ist,
Daten innerhalb des Spitzenbetriebs zu ändern , gehen
Sie dabei ein Risiko ein. Abhängig von Ihrer
Java-Version und davon, ob Ihr Terminalbetrieb die
Objekte in der Zeichenfolge verarbeiten muss oder nicht. Der Code in Peek
kann ausgeführt werden oder nicht. Seien Sie sehr vorsichtig
, wenn Sie peak für
andere Zwecke als
Debugging und Protokollierung verwenden . Hier ist ein Beispiel für seine Verwendung. In diesem Beispiel verwenden wir
peak, um einen Namen
eines Geräts zu drucken , bevor wir es weiterhin seinen Bestelldetails
zuordnen diese potenzielle
Weiterverarbeitung
sammeln. Ein Ergebnis dieses Streams
ist eine Liste von Bestelldetails. Dank des Spitzenbetriebs können
wir mit
einem Gerät, das Objekte
für die Ergebnisse verwenden, einige Protokollierung erhalten . Und ich habe gesehen, dass Peak einfach
eine Möglichkeit ist , während eines Streams etwas
Protokollierung zu erhalten. Sie können damit
Daten ändern, aber es ist riskant, daher würde ich davon abraten wenn Sie mit Streams noch nicht
vertraut sind.
11. Endabschnitt: sammeln: Unser zweiter
Terminalbetrieb, Collect. Collect ist ein
Terminalvorgang, der alle Elemente
eines Streams in etwas
sammelt. Es wird häufig verwendet, um die resultierenden Elemente eines
Streams in eine Liste aufzunehmen. Es gibt zwei Implementierungen
für diese. Die erste erwartet
eine Implementierung der
Collector-Schnittstelle. Dies ist jedoch keine funktionale
Schnittstelle und kann daher nicht
als Lambda-Ausdruck umgeschrieben werden. Zum Glück für uns, ja, Lava war so
nett,
den
Sammlerclustern
alle möglichen hilfreichen Methoden zur Verfügung den
Sammlerclustern
alle möglichen zu stellen. Viele davon
sprengen den Rahmen dieses Workshops. Aber es gibt eine, die
Sie häufig verwenden, Collectors mit zwei Listen. Wie Sie vielleicht erwarten. Dies gibt eine Implementierung der Collector-Schnittstelle zurück, alle Elemente
der Zeichenfolge in einer
Liste
sammelt und zurückgibt. Ich persönlich benutze dies 190, 9% der Fälle, in denen ich
etwas sammeln
muss , und erwarte
, dass Sie dasselbe erleben. Aber um zu verstehen,
was es ein bisschen besser macht, gehen
wir die
zweite Implementierung durch. Dieser erwartet einen Lieferanten
und zwei von Verbrauchern. Das Java-Dokument gibt uns einen schönen
Überblick darüber, was es tut. Der Lieferant liefert
uns das, was wir als
Ergebnis des Sammelvorgangs erwarten. Dann wird der erste
Fahrradverbraucher verwendet, um ein Element des
Streams in dieses Ergebnis zu
konsumieren. Dies wird fortgesetzt, bis
alle Elemente aus dem Stream oder
bis zum Endergebnis verbraucht werden. Dies zeigt uns nicht, was der zweite Bi-Verbraucher
tut, der es kombiniert, aber das
liegt daran, dass der Combiner nur benötigt
wird, wenn der
Stream parallel ausgeführt wird. In diesem Fall haben Sie möglicherweise zwei oder mehr Threads, die mit
dem Lieferanten beginnen. Nachdem alle Bedrohungen ausgeführt wurden, müssen
die Ergebnisse dieser
Threads zu einem einzigen Ergebnis
kombiniert werden einem einzigen Ergebnis
kombiniert Genau dafür wird ein
Combiner verwendet. Lassen Sie es uns in die Praxis umsetzen. Mit einem Lieferanten und
zwei von Verbrauchern. Ich werde das Sammeln
der Kundenelemente
in einem Stream replizieren . Wir beginnen mit dem Lieferanten. Bei Aufrufen wird die leere Liste angezeigt, in die wir alle Elemente des Streams einfügen. Als nächstes brauchen wir einen
Fahrradverbraucher, alle Elemente
der Saite in der Lieferliste
sammelt . Dies macht deutlich, dass unser Ergebnis tatsächlich
diese Liste ist und dass alle Kundenelemente aus dem Stream in einer Setlist
zusammengefasst werden. Im Falle eines sequenziellen Streams das das Ende. Um jedoch eine
parallele Verarbeitung zu ermöglichen, wird ein Endprodukt des Verbrauchers benötigt , das zwei
Ergebnisse zu einem kombiniert. dies vom Verbraucher verwenden, fügen
wir die Elemente
des zweiten Ergebnisses
in das erste hinzu. Dies wird
vom Stream wiederholt, bis alle Bedrohungsergebnisse wieder zu einem
zusammengeführt sind. Hier werden sie in
den Sammelvorgang eingefüllt. Und damit wissen Sie, wie Sie
Ihre eigenen Sammlungen erstellen können. Um es noch einmal zu wiederholen: Es gibt zwei Implementierungen
der Collect-Zusammenarbeit. Der erste erwartet
einen Collector, von dem Java mithilfe der
Collectors-Klausel einige Optionen
für Sie vorbereitet hat . Der zweite erwartet einen
Lieferanten und bindet die Verbraucher, wodurch er viel
anpassbarer wird. Du wirst die
meiste Zeit die vorbereitete Klasse zum
Sammeln dieser Klasse verwenden . Aber zumindest
weißt du jetzt, wie es funktioniert.
12. Endablauf: Reduzieren: Der Endbetreiber
wird die Reduzierung besprechen. Reduce ist eine
Terminaloperation, mit der alle Elemente eines
Streams in ein einzelnes Objekt
reduziert werden können . Es ist ein bisschen wie eine
Sammeloperation, die wir besprochen haben. Aber wohingegen collect alle Elemente
des Streams auf unveränderliche
Container wie eine Liste oder ein Set
anwendet . Reduce
sammelt nicht
alle Elemente, sondern wendet sie auf ein einzelnes
Objekt an. Die Identität. Reduzieren, nimmt seine Identität, Akkumulation oder Elemente
in die Identität kombiniert
dann mehrere Ergebnisse zu einem,
falls sie parallel ausgeführt werden. Reduzieren ist die am schwersten zu greifende
Operation. Fühl dich also nicht schlecht, wenn du
nicht alles auf einmal bekommst. Nehmen Sie sich Zeit, um diesem Abschnitt zu
folgen und probieren Sie die Übungen am
Ende des Unterrichts aus. Wenn Sie noch Fragen haben, können Sie diese gerne
über die Plattform stellen. Ordnung, lass uns eintauchen. Deep reduce erwartet
einen Startwert namens Identitätsfunktion,
der
als Akkumulation verwendet wird, und
einen binären Operator verwendet wird, um Ergebnisse
aus separaten Threads zu kombinieren. Der Alpha-Hund gibt uns einen schönen
Überblick darüber, was er tut. Diese Identität gibt uns
den Anfang der Ergebnisse. Es sollte eine saubere, leere Tafel sein, sodass das Hinzufügen eines
Werts aus dem Stream zu dieser Identität zu diesem Ergebnis führt
und sich als Produkt ändert. Dann wird
es für jedes Element
aus dem Stream mithilfe der Funktion by in
den resultierenden Wert akkumuliert . Genau wie beim Sammeln zeigt uns
dies nicht, was der binäre Operator,
der Combiner, tut. Das gleiche gilt hier. Der Combiner wird nur verwendet
, um die Ergebnisse
mehrerer Threads miteinander zu kombinieren mehrerer Threads miteinander falls der Stream parallel ausgeführt wird. Lassen Sie es uns in die Praxis umsetzen. In unserem Beispiel wählen
wir alle
Kunden in einer Ganzzahl aus, wählen
wir alle
Kunden in einer Ganzzahl die die Gesamtanzahl der
Geräte
darstellt , die diesen
Kunden gehören. Unsere Identität
sollte eine Ganzzahl sein. Infolgedessen wollen wir aus diesem Stream
raus. Was den Wert angeht. Denken Sie daran, dass
das Hinzufügen
eines Ergebnisses zur Identität zu
einer Änderung als Produkt führen muss. In unserem Fall
müssen wir die Größe
aller
Gerätelisten addieren aller
Gerätelisten um zu
unseren Endergebnissen zu gelangen. Welcher Wert der
Identität bedeutet, dass Identität plus Größe der
Geräteliste gleich zwei ist. Das Gerät ist
, stimmt, Null. Unsere Identität
wird also so sein. Als nächstes benötigen wir eine Funktion, die ein Element des
Streams in diese Identität
erzeugt. Das erste generische Element, das
in der Funktion by verwendet wird, ist eine Ganzzahl,
die den aktuellen Wert
der Ergebnisse darstellt . Die Zwischensumme, wenn man so will. Die zweite generische Verwendung ist
das Element des Streams, das
in die Zwischensumme reduziert wird. Die dritte generische
Verwendung ist der Typ des Ergebnisses
der
Operation, eine Ganzzahl. Dieses dritte Generikum muss mit
dem ersten identisch sein, was logisch ist, da der erste generische Begriff die Zwischensumme
darstellt. Im Falle eines sequentiellen Streams wäre
das das Ende. Um jedoch eine
parallele Verarbeitung zu ermöglichen, müssen
Sie einen binären Operator verwenden, um zwei Ergebnisse zu einem zu kombinieren. Mit diesem binären Operator kombinieren
wir die Ergebnisse
von zwei Threads miteinander. Dies wird vom
Stream wiederholt, bis alle Thread-Ergebnisse wieder zu einem
zusammengeführt sind. Hier werden sie in
den Reduziervorgang eingefüllt. Um es noch einmal zu wiederholen,
beginnen wir mit einer sauberen, leeren Tafel als Identity
Zero. In unserem Fall. Wir fügen dann die Größe
der Geräteliste
aller Elemente aus dem
Stream zu dieser Identität hinzu. Wenn der Stream parallel ausgeführt wird, kombinieren
wir sie, um zwei Zwischensummen zu einer
zu addieren , bis wir
ein einziges Ergebnis übrig haben. In unserem Beispiel haben wir
unsere komplexen Objekte auf eine
einzige Ganzzahl reduziert . Aber reduced kann auch verwendet
werden, um
alle Elemente eines Streams in
ein einziges komplexes Objekt zu reduzieren . Zum Beispiel könnten wir
unsere Kunden auf einen einzigen
Kunden reduzieren , der unsere Informationen enthält. Aber ich lasse das
als Teil
der Übungen, die du alleine machen
kannst.
13. Zusammenfassung und Afterword: Wir sind
am Ende dieses Kurses angelangt. Lassen Sie uns zusammenfassen, um zu sehen,
was wir auf
unserer Reise in die
Java Eight Streams API gelernt haben . Wir haben angefangen zu
lernen, wofür Streams verwendet
werden und wie sie funktionieren. Wir haben dann aufgeschlüsselt, wie Sie dank
funktionaler Schnittstellen einen
Lambda-Ausdruck aus
einer anonymen inneren Klasse
erstellen können einen
Lambda-Ausdruck aus
einer anonymen inneren Klasse
erstellen . Wir haben kurz besprochen, aber
funktionale Schnittstellen sind es und welche Sie
häufig bei der Arbeit mit Streams verwenden. Schließlich gingen wir auf häufig verwendete Zwischen
- und Terminaloperationen ein. Danke, dass du dir
diesen Kurs angesehen hast. Ich hoffe, Sie haben genug
über Streams gelernt, um
Situationen zu erkennen , in denen Sie sie verwenden können
und können. Wenn Sie mir helfen möchten, diesen Kurs
zu verbessern und uns
mitzuteilen, ob
Sie ihn nützlich fanden oder nicht. Ich wäre Ihnen dankbar, wenn Sie
eine Bewertung abgeben würden. Wie versprochen. Ich habe einige
Übungen für Sie vorbereitet, um
zu sehen, ob Sie das in diesem Kurs erlernte
Wissen
anwenden können . Jede Übung hat eine Anfrage, ein erwartetes Ergebnis,
damit Sie überprüfen können, ob Sie es richtig gemacht haben. Wenn es
Unklarheiten über diese bereits
angebotenen Übungen gibt, können Sie mir gerne eine
Nachricht mit Ihren Fragen senden. Ich helfe Ihnen nach
besten Kräften. Noch einmal. Danke fürs Zuschauen
und bis zum nächsten Mal.