Einführung in Lambda
Lambda -Ausdrücke sind ein wichtiges neues Merkmal in Java SE 8. Lambda -Ausdrücken ermöglichen es Ihnen, funktionale Schnittstellen durch Ausdrücke zu ersetzen. Der Lambda -Ausdruck ähnelt genau der Methode, die eine normale Parameterliste und einen Körper (Körper, der ein Ausdruck oder ein Codeblock) enthält, der diese Parameter verwendet.
Lambda -Ausdrücke verbessern auch die Sammlungsbibliothek. Java SE 8 fügt 2 Pakete hinzu, die Stapelvorgänge für Sammeldaten betreiben: java.util.function Paket und java.util.stream -Paket. Ein Stream ist wie ein Iterator, aber mit vielen zusätzlichen Funktionen. Im Allgemeinen sind Lambda -Ausdrücke und Streams die größten Veränderungen, da die Java -Sprache Generika und Anmerkungen hinzufügt.
Lambda -Ausdrücke sind im Wesentlichen anonyme Methoden, und ihre zugrunde liegende Ebene wird durch invokedynamic Richtlinien implementiert, um anonyme Klassen zu generieren. Es bietet eine einfachere Syntax- und Schreibmethode, mit der Sie funktionale Schnittstellen durch Ausdrücke ersetzen können. In den Augen einiger Leute kann Lambda Ihren Code prägnanter machen und ihn überhaupt nicht verwenden - diese Ansicht ist sicherlich in Ordnung, aber das Wichtigste ist, dass Lambda Java Schließungen bringt. Dank der Unterstützung von Lamdba für Sammlungen hat Lambda die Leistung beim Durchqueren von Sammlungen unter Multi-Core-Prozessorbedingungen erheblich verbessert. Darüber hinaus können wir Sammlungen in Form von Datenströmen verarbeiten - was sehr attraktiv ist.
Lambda -Syntax
Die Syntax von Lambda ist extrem einfach, ähnlich der folgenden Struktur:
(Parameter) -> Ausdruck
oder
(Parameter) -> {Anweisungen; }Lambda -Ausdrücke bestehen aus drei Teilen:
1. Paramate: Eine Liste formaler Parameter in ähnlichen Methoden sind die Parameter hier Parameter in der Funktionsschnittstelle. Die Parametertypen hier können explizit deklariert oder nicht deklariert werden, aber implizit durch die JVM abgeleitet werden. Wenn es nur einen Inferenztyp gibt, können Klammern weggelassen werden.
2. ->: Es kann als "verwendet werden" verstanden werden
3. Method Body: Es kann ein Ausdruck oder ein Codeblock sein, es ist die Implementierung der Methode in der funktionalen Schnittstelle. Ein Codeblock kann einen Wert oder eine Umkehrung zurückgeben. Der Codeblock entspricht dem Methodenkörper der Methode. Wenn es sich um einen Ausdruck handelt, können Sie auch einen Wert zurückgeben oder nichts zurückgeben.
Verwenden wir die folgenden Beispiele, um zu veranschaulichen:
// Beispiel 1: Keine Notwendigkeit, Parameter zu akzeptieren, direkt 10 ()-> 10 // Beispiel 2: Akzeptieren Sie zwei Parameter des Int-Typs und senden Sie die Summe dieser beiden Parameter zurück (int x, int y)-> x+y; // Beispiel 2: Akzeptieren Sie zwei Parameter x und y, der Typ dieser Parameter (y). 3: Akzeptieren Sie eine Zeichenfolge und drucken Sie die Zeichenfolge, um das Ergebnis ohne Umkehrung des Ergebnisses (String-Name)-> System.out.println (Name); // Beispiel 4: Akzeptieren Sie einen Parameternamen des abgeleiteten Typs und drucken Sie die Zeichenfolge zu Konsolenname-> System.out.Out.println (Name); // Beispiel 5: Akzeptieren Sie zwei Zeichensparameter und geben Sie sie separat aus. Sex)-> {System.out.println (Name); System.out.println (Sex)} // Beispiel 6: Akzeptieren Sie einen Parameter x und geben Sie doppelt so den Parameter x-> 2*x zurückWo man lambda benutzt
In [funktionaler Schnittstelle] [1] wissen wir, dass der Zieltyp des Lambda -Expression eine funktionale Schnittstelle ist - jede Lambda kann mit einem bestimmten Typ über eine bestimmte funktionale Schnittstelle übereinstimmen. Daher kann ein Lambda -Ausdruck überall angewendet werden, wo er dem Zieltyp entspricht. Der Lambda -Expression muss den gleichen Parametertyp wie die abstrakte Funktionsbeschreibung der funktionalen Schnittstelle haben, sein Rückgabetyp muss auch mit dem Rückgabetyp der abstrakten Funktion kompatibel sein, und die Ausnahmen, die er werfen kann, sind auf den Funktionsbeschreibungsbereich begrenzt.
Schauen wir uns als nächstes ein benutzerdefiniertes Beispiel für funktionale Schnittstellen an:
@FunctionAlinInterface Interface Converter <f, t> {t convert (f from);}Verwenden Sie zunächst die Schnittstelle auf traditionelle Weise:
Converter <String, Integer> Converter = New Converter <String, Integer> () {@Override public Integer Convert (String von) {return Integer.ValueOf (von); }}; Integer result = converter.convert ("200"); System.out.println (Ergebnis);Offensichtlich gibt es kein Problem damit. Das nächste ist der Moment, in dem Lambda auf dem Feld kommt und Lambda verwendet, um die Konverterschnittstelle zu implementieren:
Converter <String, Integer> Converter = (Param) -> Integer.ValueOf (Param); Integer result = converter.convert ("101"); System.out.println (Ergebnis);Im obigen Beispiel denke ich, dass Sie ein einfaches Verständnis für die Verwendung von Lambda haben. Im Folgenden verwenden wir einen häufig verwendeten Runnable, um zu demonstrieren:
In der Vergangenheit haben wir diesen Code vielleicht geschrieben:
neuer Thread (new Runnable () {@Override public void run () {System.out.println ("Hallo Lambda");}}). start ();In einigen Fällen kann eine große Anzahl anonymer Klassen den Code überfüllt erscheinen lassen. Jetzt können Sie Lambda verwenden, um es einfach zu machen:
neuer Thread (() -> System.out.println ("Hallo Lambda")). start ();Methodenreferenz
Methodenreferenz ist eine vereinfachte Möglichkeit, Lambda -Ausdrücke zu schreiben. Die verwiesene Methode ist tatsächlich eine Implementierung des Methodenkörper des Lambda -Expression, und seine Syntaxstruktur lautet:
Objectref :: methodName
Die linke Seite kann der Name der Klassennamen oder Instanz sein, die Mitte ist das Methodenreferenzsymbol "::" und die rechte Seite ist der entsprechende Methodenname.
Methodenreferenzen sind in drei Kategorien unterteilt:
1. Referenz zur Statischen Methode
In einigen Fällen können wir einen solchen Code schreiben:
public class Referencetest {public static void main (String [] args) {Converter <String, Integer> Converter = New Converter <String, Integer> () {@Override public Integer Convert (String from) {return Referencetest.string2int (from); }}; Converter.Convert ("120"); } @FunctionAlalInterface Interface Converter <f, t> {t convert (f from); } static int string2int (String von) {return Integer.ValueOf (von); }}Zu diesem Zeitpunkt ist der Code, wenn Sie statische Referenzen verwenden, prägnanter:
Converter <String, Integer> Converter = Referencetest :: String2int; Converter.Convert ("120");2. Referenz für Instanzmethoden
Wir könnten auch einen solchen Code schreiben:
public class Referencetest {public static void main (String [] args) {Converter <String, Integer> Converter = New Converter <String, Integer> () {@Override public Integer Convert (String from) {return New Helper (). String2int (from); }}; Converter.Convert ("120"); } @FunctionAlalInterface Interface Converter <f, t> {t convert (f from); } statische Klasse Helper {public int string2int (String von) {return Integer.ValueOf (von); }}}Außerdem erscheint die Verwendung von Beispielmethoden zur Referenz präzise:
Helfer Helper = New Helper (); Converter <String, Integer> Converter = HELPER :: STRING2INT; Converter.Convert ("120");3. Referenz für Konstruktionsmethoden
Lassen Sie uns nun Verweise auf Konstrukteure zeigen. Zuerst definieren wir ein Tierklassentier:
Klasse Animal {privater String -Name; privates int Alter; public Animal (String -Name, int age) {this.name = name; this.age = Alter; } public void Behavior () {}} Als nächstes definieren wir zwei Unterklassen des Tieres: Dog、Bird
Public Class Bird erweitert das Tier (public Bird (Stringname, int Alter) {Super (Name, Alter); } @Override public void Behavior () {System.out.println ("Fly"); }} Klasse Dog erweitert Animal {public hund (String name, int age) {Super (Name, Alter); } @Override public void Behavior () {System.out.println ("run"); }}Dann definieren wir die Fabrikschnittstelle:
Interface Factory <T erweitert Animal> {t create (String -Name, int ay); }Als nächstes werden wir die traditionelle Methode verwenden, um Objekte von Hunde- und Vogelklassen zu erstellen:
Factory factory = new factory () {@Override public Animal create (String -Name, int age) {neuer Hund zurückgeben (Name, Alter); }}; factory.create ("alias", 3); factory = new factory () {@Override public Animal Create (String -Name, int Alter) {neuer Vogel zurückgeben (Name, Alter); }}; factory.create ("smook", 2);Ich habe mehr als zehn Codes geschrieben, um zwei Objekte zu erstellen. Versuchen wir nun die Konstruktorreferenz:
Fabrik <Antiere> dogfactory = hunde :: neu; Animal Dog = DogFactory.create ("alias", 4); FACTORY <Bird> BirdFactory = Bird :: New; Bird Bird = BirdFactory.create ("Smook", 3); Auf diese Weise erscheint der Code sauber und ordentlich. Wenn Sie Dog::new zum Eindringen von Objekten verwenden, wählen Sie die entsprechende Erstellungsfunktion, indem Sie die Factory.create -Funktion unterschreiben.
Lambdas Domain- und Zugriffsbeschränkungen
Die Domäne ist der Umfang, und die Parameter in der Parameterliste im Lambda -Expression sind im Rahmen des Lambda -Ausdrucks (Domäne) gültig. Im Lambda -Expression können auf externe Variablen zugegriffen werden: Lokale Variablen, Klassenvariablen und statische Variablen, aber der Grad der Betriebsbeschränkungen ist unterschiedlich.
Zugang zu lokalen Variablen
Lokale Variablen außerhalb des Lambda -Ausdrucks werden implizit vom JVM zum endgültigen Typ zusammengestellt, sodass nur auf sie zugegriffen werden kann, aber nicht geändert werden kann.
öffentliche Klasse Referencetest {public static void main (String [] args) {int n = 3; Calculate Calculate = param -> {// n = 10; Fehlerrückgabe N + Param; }; berechnen.calculate (10); } @FunctionAminInterface Schnittstelle berechnen {int calculate (int value); }}Zugang zu statischen und Mitgliedsvariablen
In Lambda -Ausdrücken sind statische und Mitgliedsvariablen lesbar und beschreibbar.
öffentliche Klasse Referencetest {public int count = 1; public static int num = 2; public void test () {calculate calculate = param -> {num = 10; // Die statische Variable count = 3; // Die Member -Variable return n + param; }; berechnen.calculate (10); } public static void main (String [] args) {} @FunctionAnInterface Schnittstelle Berechnen Sie {int calculate (int value); }}Lambda kann nicht auf die Standardmethode der Funktionsschnittstelle zugreifen
Java8 verbessert Schnittstellen, einschließlich Standardmethoden, mit denen Standard -Keyword -Definitionen zu Schnittstellen hinzufügen können. Wir müssen hier beachten, dass der Zugriff auf Standardmethoden intern nicht unterstützt wird.
Lambda -Praxis
In dem Abschnitt [funktionaler Schnittstelle] [2] haben wir erwähnt, dass viele funktionale Schnittstellen in das Paket java.util.function integriert sind, und nun werden wir die häufig verwendeten funktionalen Schnittstellen erklären.
Prädikatschnittstelle
Geben Sie einen Parameter ein und geben Sie einen Boolean Wert zurück, der viele Standardmethoden für das logische Beurteilung enthält:
@Test public void PredictTest () {Predicate <string> predict = (s) -> S.Length ()> 0; Boolean Test = Predict.test ("Test"); System.out.println ("Stringlänge ist größer als 0:" + test); test = Predict.test (""); System.out.println ("Stringlänge ist größer als 0:" + test); Prädikat <Object> pre = Objects :: nonnull; Objekt ob = null; test = Pre.Test (OB); System.out.println ("Objekt ist nicht leer:" + test); ob = neues Objekt (); test = Pre.Test (OB); System.out.println ("Objekt ist nicht leer:" + test); }Funktionsschnittstelle
Empfangen Sie einen Parameter und geben Sie ein einzelnes Ergebnis zurück. Die Standardmethode ( andThen ) kann mehrere Funktionen zusammenstellen, um eine zusammengesetzte Funtion (mit Eingabe, Ausgabe) zu bilden.
@Test public void functionTest () {Funktion <String, Integer> toIteger = Integer :: ValueOf; // Das Ausführungsergebnis von Tointer wird als Eingabe für die zweite BackToString -Funktion <String, String> BackToString = tointeger.und (string :: valueOf) verwendet. String result = BackToString.Apply ("1234"); System.out.println (Ergebnis); Funktion <Integer, Integer> add = (i) -> {System.out.println ("Frist Input:" + i); Rückkehr i * 2; }; Funktion <Integer, Integer> Zero = add.andThen ((i) -> {System.out.println ("zweite Eingabe:" + i); return i * 0;}); Integer res = null.Apply (8); System.out.println (res); }Lieferantenschnittstelle
Gibt ein Ergebnis eines bestimmten Typs zurück. Im Gegensatz zur Function muss Supplier Parameter nicht akzeptieren (Lieferant, mit Ausgabe, aber ohne Eingabe)
@Test public void SupplieTest () {Lieferant <string> Lieferant = () -> "Spezialtypwert"; String S = Lieferant.get (); System.out.println (s); }Verbraucherschnittstelle
Repräsentiert die Operationen, die mit einem einzelnen Eingabeparameter durchgeführt werden müssen. Im Gegensatz zur Function gibt Consumer keinen Wert zurück (Verbraucher, Eingabe, keine Ausgabe).
@Test public void ConsumerTest () {Consumer <Integer> add5 = (p) -> {System.out.println ("Old Value:" + P); p = p + 5; System.out.println ("neuer Wert:" + p); }; add5.accept (10); } Die Verwendung der oben genannten vier Schnittstellen repräsentiert die vier Typen im Paket java.util.function . Nach dem Verständnis dieser vier funktionalen Schnittstellen sind andere Schnittstellen leicht zu verstehen. Machen wir nun eine einfache Zusammenfassung:
Predicate wird für das logische Beurteilung verwendet, Function wird an Stellen verwendet, an denen Eingänge und Ausgänge vorhanden sind, Supplier an Stellen verwendet wird, an denen keine Eingabe und Ausgaben vorhanden sind, und Consumer wird an Stellen verwendet, an denen Eingaben und keine Ausgänge vorhanden sind. Sie können die Nutzungsszenarien basierend auf der Bedeutung seines Namens kennen.
Strom
Lambda bringt Verschlüsse für Java 8 mit, was besonders wichtig ist, was für den Sammeloperationen wichtig ist: Java 8 unterstützt funktionale Operationen auf dem Strom von Sammelobjekten. Darüber hinaus ist die Stream -API auch in die Sammel -API integriert, wodurch Stapeloperationen auf Sammlungsobjekten ermöglicht werden.
Lernen wir Stream kennen.
Stream repräsentiert einen Datenstrom. Es hat keine Datenstruktur und speichert selbst keine Elemente. Seine Operationen werden den Quellstrom nicht ändern, sondern einen neuen Stream generieren. Als Schnittstelle zum Betrieb von Daten bietet es Filterung, Sortierung, Zuordnung und Regulierung. Diese Methoden werden gemäß dem Rückgabetyp in zwei Kategorien unterteilt: jede Methode, die den Stromtyp zurückgibt, wird als Zwischenmethode (Zwischenoperation) bezeichnet, und der Rest sind Abschlussmethoden (vollständiger Betrieb). Die Abschlussmethode gibt einen Wert eines Typs zurück, während die Zwischenmethode einen neuen Stream zurückgibt. Der Aufruf von Zwischenmethoden wird normalerweise gekettet und der Prozess bildet eine Pipeline. Wenn die endgültige Methode aufgerufen wird, wird der Wert sofort aus der Pipeline verbraucht. Hier müssen wir uns erinnern: Stream -Operationen werden so "verzögert" wie möglich ausgeführt, was wir oft als "faulen Operationen" bezeichnen, was dazu beiträgt, die Nutzung der Ressourcen zu verringern und die Leistung zu verbessern. Für alle Zwischenoperationen (außer sortiert) werden sie im Verzögerungsmodus ausgeführt.
Stream bietet nicht nur leistungsstarke Funktionen zur Datenbetrieb, sondern auch, sondern auch, dass Stream sowohl serielle als auch Parallelität unterstützt. Durch die Parallelität kann Stream eine bessere Leistung bei Multi-Core-Prozessoren haben.
Der Verwendung des Streams hat ein festes Muster:
1. Erstellen Sie einen Stream
2. "Ändern" durch Zwischenoperationen den ursprünglichen Stream und generieren Sie einen neuen Stream
3.. Verwenden Sie den Abschlussvorgang, um das Endergebnis zu generieren
Das heißt
Erstellen -> ändern -> vollständig
Schaffung von Stream
Für eine Sammlung kann sie erstellt werden, indem sie den stream() oder parallelStream() der Sammlung nennt. Darüber hinaus werden diese beiden Methoden auch in der Sammlungsschnittstelle implementiert. Für Arrays können sie durch die statische Methode of(T … values) . Darüber hinaus bietet Arrays auch Streams.
Zusätzlich zum Erstellen von Streams basierend auf Sammlungen oder Arrays oben können Sie auch einen leeren Stream über Steam.empty() erstellen oder Streams generate() verwenden, um unendliche Streams zu erstellen.
Nehmen wir einen seriellen Strom als Beispiel, um mehrere häufig verwendete Zwischen- und Abschlussmethoden des Streams zu veranschaulichen. Erstellen Sie zuerst eine Listensammlung:
List <String> lists = new ArrayList <string> (); lists.add ("a1"); lists.add ("a2"); lists.add ("b1"); lists.add ("b2"); lists.add ("b3"); lists.add ("o1");Zwischenmethode
Filter
In Kombination mit der Prädikatschnittstelle filtern Filter alle Elemente im Streaming -Objekt. Dieser Vorgang ist ein Zwischenbetrieb, was bedeutet, dass Sie andere Vorgänge basierend auf dem von der Operation zurückgegebenen Ergebnis ausführen können.
public static void streamFilterTest () {lists.stream (). Filter ((s -> s.Startswith ("a")). foreach (System.out :: println); // äquivalent mit der obigen Operation Predicate <String> Prädikat = (s) -> s.Startswith ("a"); lists.Stream (). Filter (Prädikat) .foreach (System.out :: println); // kontinuierliches Filterungsprädikat <string> Prädikat1 = (S -> S.endswith ("1")); lists.Stream (). Filter (Prädikat) .Filter (Prädikat1) .foreach (System.out :: println); }Sortieren (sortiert)
In Kombination mit der Komparatorschnittstelle gibt dieser Vorgang eine Ansicht des sortierten Streams zurück, und die Reihenfolge des ursprünglichen Streams ändert sich nicht. Die Kollationsregeln werden über den Komparator angegeben, und die Standardeinstellung besteht darin, sie in natürlicher Reihenfolge zu sortieren.
public static void streamSortedTest () {System.out.println ("Standard -Komparator"); lists.Stream (). sortiert (). filter ((s -> s.Startswith ("a")). foreach (System.out :: println); System.out.println ("benutzerdefinierter Vergleicher"); lists.stream (). sortiert ((p1, p2) -> p2.comPareto (p1)). Filter ((s -> s.startswith ("a")). foreach (System.out :: println); }Karte (Karte)
In Kombination mit der Function kann dieser Vorgang jedes Element im Stream -Objekt in ein anderes Element zuordnen und die Konvertierung von Elementtypen erkennen.
public static void streammaptest () {lists.stream (). map (string :: touppercase) .sortiert ((a, b) -> b.comPareto (a)). foreach (system.out :: println); System.out.println ("benutzerdefinierte Zuordnungsregeln"); Funktion <string, string> function = (p) -> {return p + ".txt"; }; lists.stream (). map (string :: touppercase) .map (Funktion) .Sortiert ((a, b) -> b.comPareto (a)). foreach (System.out :: println); }Die obigen Einführung führt kurz drei häufig verwendete Operationen ein, die die Verarbeitung der Sammlung erheblich vereinfachen. Als nächstes stellen wir verschiedene Möglichkeiten vor, um zu vervollständigen:
Veredelungsmethode
Nach dem "Transformation" -Prozess muss das Ergebnis erzielt werden, dh der Vorgang wird abgeschlossen. Schauen wir uns die verwandten Operationen unten an:
Übereinstimmen
Wird verwendet, um zu bestimmen, ob ein predicate dem Stream -Objekt übereinstimmt, und gibt beispielsweise ein Boolean Ergebnisergebnis zurück:
public static void StreamMatchTest () {// Return true, solange ein Element im Stream -Objekt boolean anystartwitha = lista.stream () übereinstimmt. System.out.println (AnystartWitha); // true zurückgeben, wenn jedes Element im Stream -Objekt den blooleschen Allstartwitha = lista.stream () übereinstimmt. System.out.println (Allstartwitha); }Sammeln
Nach der Transformation sammeln wir die Elemente des transformierten Streams, wie zum Beispiel das Speichern dieser Elemente in eine Sammlung. Zu diesem Zeitpunkt können wir beispielsweise die von Stream bereitgestellte Sammelmethode verwenden:
public static void streamCollectTest () {list <string> list = lists.stream (). filter ((p) -> p.StartsWith ("a")). sortiert () sammeln (collectors.tolist ()); System.out.println (Liste); }Zählen
SQL-ähnliche Anzahl wird verwendet, um die Gesamtzahl der Elemente im Stream zu zählen, z. B.:
public static void streamCountTest () {long count = lists.stream (). filter ((s -> s.Startswith ("a")). count (); System.out.println (count); }Reduzieren
Mit reduce können wir Elemente auf unsere eigene Weise berechnen oder Elemente in einem Stream mit einem Muster zusammenstellen, zum Beispiel:
public static void streamReducetest () {optional <string> optional = lists.stream (). sortEd (). Reduzieren ((s1, s2) -> {System.out.println (S1 + "|" + S2); Rückgabe s1 + "|" + S2;}); }Die Ausführungsergebnisse sind wie folgt:
a1 | a2a1 | a2 | b1a1 | a2 | b1 | b2a1 | a2 | b1 | b2 | b3a1 | a2 | b1 | b2 | b3 | o1
Parallelstrom gegen Serienstrom
Bisher haben wir die häufig verwendeten Zwischen- und Abschlussoperationen eingeführt. Natürlich basieren alle Beispiele auf dem seriellen Strom. Als nächstes werden wir das wichtige Drama - Parallel Stream (Parallel Stream) vorstellen. Der Parallelstream wird basierend auf dem fork-join-Parallel-Zerlegungs-Framework implementiert und den Big-Data-Set in mehrere kleine Daten unterteilt und übergibt sie für die Verarbeitung an verschiedene Threads. Auf diese Weise wird die Leistung unter der Situation der Multi-Core-Verarbeitung erheblich verbessert. Dies steht im Einklang mit dem Entwurfskonzept von MapReduce: Große Aufgaben werden kleiner und kleine Aufgaben werden für die Ausführung unterschiedlichen Maschinen neu zugeordnet. Aber die kleine Aufgabe hier wird an verschiedene Prozessoren übergeben.
Erstellen Sie einen parallelen Strom über parallelStream() . Um zu überprüfen, ob parallele Streams die Leistung wirklich verbessern können, führen wir den folgenden Testcode aus:
Erstellen Sie zuerst eine größere Sammlung:
Liste <String> biglists = new ArrayList <> (); für (int i = 0; i <10000000; i ++) {uUid uUid = uUid.randomuuid (); Biglists.Add (uUid.toString ()); }Testen Sie die Zeit, um unter seriellen Streams zu sortieren:
private static void nictparallelStreamSortEdTest (List <string> biglists) {Long start time = system.nanotime (); Long Count = Biglists.Stream (). sortiert (). count (); Long Endzeit = System.nanotime (); Long Millis = TimeUnit.Nanoseconds.tomillis (Endime - StartTime); System.out.println (System.out.printf ("Serielle Sortierung: %d ms", Millis)); }Testen Sie die Zeit, um in parallelen Strömen zu sortieren:
private static void parallelsstreamsortedTest (list <string> biglists) {long start time = system.nanotime (); long count = biglists.ParallelStream (). sortiert (). count (); Long Endzeit = System.nanotime (); Long Millis = TimeUnit.Nanoseconds.tomillis (Endime - StartTime); System.out.println (System.out.printf ("Parallelsorting: %d ms", Millis)); }Die Ergebnisse sind wie folgt:
Serielle Sortierung: 13336 ms
Parallele Sortierung: 6755 ms
Nachdem wir dies gesehen hatten, stellten wir fest, dass sich die Leistung um etwa 50%verbessert hat. Denken Sie auch, dass Sie in Zukunft parallel Stream verwenden können? Tatsächlich ist es nicht der Fall. Wenn Sie jetzt noch ein Einzelkernprozessor sind und das Datenvolumen nicht groß ist, ist das serielle Streaming immer noch eine gute Wahl. Sie werden auch feststellen, dass in einigen Fällen die Leistung von Serienströmen besser ist. Was die spezifische Verwendung betrifft, müssen Sie es zuerst testen und dann nach dem tatsächlichen Szenario entscheiden.
Fauler Betrieb
Oben haben wir über Stream gesprochen, das so spät wie möglich läuft, und hier erklären wir ihn, indem wir einen unendlichen Stream erstellen:
Verwenden Sie zunächst die Methode zur Erzeugung von Stream generate , um eine natürliche Zahlensequenz zu erstellen, und transformieren dann den Stream durch map :
// Inkrementelle Sequenzklasse NatureSeq implementiert Lieferanten <Long> {long value = 0; @Override public long get () {value ++; Rückgabewert; }} public void streamCreateTest () {Stream <Long> Stream = Stream.Generate (new NatureSeq ()); System.out.println ("Anzahl der Elemente:"+stream.map ((param) -> {return param;}). Limit (1000) .Count ()); }Das Ausführungsergebnis ist:
Anzahl der Elemente: 1000
Wir fanden heraus, dass alle Zwischenoperationen (z. B. filter,map usw., aber sorted nicht ausgeführt werden können) in Ordnung. Das heißt, der Prozess der Durchführung von Zwischenoperationen im Stream und des Überlebens eines neuen Streams wird nicht sofort wirksam (oder der map in diesem Beispiel wird für immer ausgeführt und blockiert), und der Stream beginnt zu berechnen, wenn die Abschlussmethode auftritt. Konvertieren Sie diesen unendlichen Strom durch limit() in einen endlichen Strom.
Zusammenfassen
Das obige ist der gesamte Inhalt der schnellen Einführung in Java Lambda. Haben Sie nach dem Lesen dieses Artikels ein tieferes Verständnis von Java Lambda? Ich hoffe, dieser Artikel wird für alle hilfreich sein, Java Lambda zu lernen.