Heim  >  Artikel  >  Java  >  Was sind die Verwendungszwecke von Lambda-Ausdrücken, die neuen Funktionen von Java 8 (Anwendungsbeispiele)

Was sind die Verwendungszwecke von Lambda-Ausdrücken, die neuen Funktionen von Java 8 (Anwendungsbeispiele)

高洛峰
高洛峰Original
2017-01-23 15:12:301519Durchsuche

Wir haben lange darauf gewartet, dass Lambda das Konzept der Schließung nach Java bringt, aber wenn wir es nicht in Sammlungen verwenden, werden wir viel Wert verlieren. Das Problem der Migration bestehender Schnittstellen zum Lambda-Stil wurde durch Standardmethoden gelöst. In diesem Artikel werden wir die Massendatenoperation (Massenoperation) in Java-Sammlungen eingehend analysieren und das Geheimnis der mächtigsten Rolle von Lambda lüften.

1. Über JSR335

JSR ist die Abkürzung für Java Specification Requests, was Java Specification Request bedeutet. Die Hauptverbesserung der Java 8-Version ist das Lambda-Projekt (JSR 335), dessen Zweck es ist um Java einfacher zu machen, Code für Mehrkernprozessoren zu schreiben. JSR 335 = Lambda-Ausdruck + Schnittstellenverbesserung (Standardmethode) + Batch-Datenoperation. Zusammen mit den beiden vorherigen Artikeln haben wir den relevanten Inhalt von JSR335 vollständig kennengelernt.

2. Externe vs. interne Iteration

In der Vergangenheit konnten Java-Sammlungen keine interne Iteration ausdrücken, sondern boten nur eine Möglichkeit der externen Iteration, nämlich eine for- oder while-Schleife.

List persons = asList(new Person("Joe"), new Person("Jim"), new Person("John"));
for (Person p :  persons) {
   p.setLastName("Doe");
}

Das obige Beispiel ist unser vorheriger Ansatz, die sogenannte externe Iteration. Die Schleife ist eine feste Sequenzschleife. Wenn wir im heutigen Multi-Core-Zeitalter die Schleife parallelisieren möchten, müssen wir den obigen Code ändern. Wie stark die Effizienz verbessert werden kann, ist noch ungewiss und birgt bestimmte Risiken (Probleme mit der Thread-Sicherheit usw.).

Um die interne Iteration zu beschreiben, müssen wir eine Klassenbibliothek wie Lambda verwenden. Verwenden wir Lambda und Collection.forEach, um die obige Schleife neu zu schreiben

persons.forEach(p->p.setLastName("Doe"));

Jetzt steuert die JDK-Bibliothek die Schleife Nr , wir müssen uns nicht darum kümmern, wie der Nachname für jedes Personenobjekt festgelegt wird. Die Bibliothek kann je nach laufender Umgebung entscheiden, wie dies geschieht: parallel, außerhalb der Reihenfolge oder verzögertes Laden. Dies ist eine interne Iteration und der Client übergibt das Verhalten p.setLastName als Daten an die API.

Die interne Iteration hängt eigentlich nicht eng mit der Stapeloperation der Sammlung zusammen. Dadurch können wir die Änderungen im grammatikalischen Ausdruck spüren. Das wirklich Interessante an Batch-Operationen ist die neue Stream-API. Das neue Paket java.util.stream wurde zu JDK 8 hinzugefügt.


3.Stream API

Stream stellt nur einen Datenstrom dar und hat keine Datenstruktur, sodass er nach einmaligem Durchlaufen nicht mehr vorhanden sein kann verwendet Traversal (Sie müssen beim Programmieren darauf achten, im Gegensatz zu Collection sind immer noch Daten darin, egal wie oft sie durchlaufen werden). Die Quelle kann Collection, Array, io usw. sein.

3.1 Zwischen- und Endpunktmethoden

Die Flow-Funktion soll eine Schnittstelle für den Betrieb von Big Data bereitstellen und Datenoperationen einfacher und schneller machen. Es verfügt über Methoden wie Filtern, Zuordnen und Reduzieren der Anzahl von Durchläufen. Diese Methoden sind in zwei Typen unterteilt: Zwischenmethoden und Terminalmethoden. Die „Stream“-Abstraktion sollte von Natur aus kontinuierlich sein Wir möchten das Endergebnis erhalten. Wenn ja, müssen Endpunktoperationen verwendet werden, um die vom Stream erzeugten Endergebnisse zu sammeln. Der Unterschied zwischen diesen beiden Methoden besteht darin, dass sie sich den Rückgabewert ansehen. Wenn es sich um einen Stream handelt, handelt es sich um eine Zwischenmethode, andernfalls handelt es sich um eine Endmethode. Weitere Informationen finden Sie in der Stream-API.

Eine kurze Einführung in mehrere Zwischenmethoden (Filter, Karte) und Endpunktmethoden (Sammeln, Summe)

3.1.1Filter

Der erste Schritt ist die Implementierung Filterfunktion im Datenstrom Die natürlichste Operation, die wir uns vorstellen können. Die Stream-Schnittstelle stellt eine Filtermethode bereit, die eine Predicate-Implementierung akzeptiert, die einen Vorgang darstellt, um einen Lambda-Ausdruck zu verwenden, der Filterbedingungen definiert.

List persons = …
Stream personsOver18 = persons.stream().filter(p -> p.getAge() > 18);//过滤18岁以上的人

3.1.2Map

Angenommen, wir filtern jetzt einige Daten, beispielsweise beim Konvertieren von Objekten. Mit der Map-Operation können wir eine Implementierung einer Funktion ausführen (das generische T und R von Function43ca9160a1fbc6e1e17f36fac17e2094 stellen die Ausführungseingabe bzw. das Ausführungsergebnis dar), die Eingabeparameter akzeptiert und zurückgibt. Sehen wir uns zunächst an, wie man sie als anonyme innere Klasse beschreibt:

Stream adult= persons
              .stream()
              .filter(p -> p.getAge() > 18)
              .map(new Function() {
                  @Override
                  public Adult apply(Person person) {
                     return new Adult(person);//将大于18岁的人转为成年人
                  }
              });

Konvertieren Sie nun das obige Beispiel in einen Lambda-Ausdruck:

Stream map = persons.stream()
                    .filter(p -> p.getAge() > 18)
                    .map(person -> new Adult(person));

3.1.3Count

Die Zählmethode ist die Endpunktmethode eines Streams, die die endgültigen Statistiken der Stream-Ergebnisse erstellen und int zurückgeben kann. Berechnen wir beispielsweise die Gesamtzahl der Personen ab 18 Jahren:

int countOfAdult=persons.stream()
                       .filter(p -> p.getAge() > 18)
                       .map(person -> new Adult(person))
                       .count();

3.1 .4Collect

Die Collect-Methode ist auch eine Endpunktmethode des Streams, die die Endergebnisse sammeln kann

List adultList= persons.stream()
                       .filter(p -> p.getAge() > 18)
                       .map(person -> new Adult(person))
                       .collect(Collectors.toList());

Oder, wenn wir eine bestimmte verwenden möchten Implementierungsklasse zum Sammeln der Ergebnisse:

List adultList = persons
                 .stream()
                 .filter(p -> p.getAge() > 18)
                 .map(person -> new Adult(person))
                 .collect(Collectors.toCollection(ArrayList::new));

Der Platz ist begrenzt, daher werden andere Zwischenmethoden und Endpunktmethoden nicht einzeln eingeführt. Nachdem Sie die obigen Beispiele gelesen haben, müssen Sie nur die verstehen Unterschied zwischen diesen beiden Methoden, und Sie können entscheiden, sie später entsprechend Ihren Anforderungen zu verwenden.

3.2 Sequentielle und parallele Streams

Jeder Stream verfügt über zwei Modi: sequentielle Ausführung und parallele Ausführung.
Sequentieller Fluss:

List <Person> people = list.getStream.collect(Collectors.toList());

Parallelfluss:

List <Person> people = list.getStream.parallel().collect(Collectors.toList());

Wie der Name schon sagt, lesen Sie bei Verwendung der sequentiellen Methode zum Durchlaufen jedes Element, bevor Sie das nächste Element lesen. Bei Verwendung der parallelen Durchquerung wird das Array in mehrere Segmente unterteilt, die jeweils in einem anderen Thread verarbeitet werden, und die Ergebnisse werden dann gemeinsam ausgegeben.

3.2.1 Parallel-Stream-Prinzip:

List originalList = someData;
split1 = originalList(0, mid);//将数据分小部分
split2 = originalList(mid,end);
new Runnable(split1.process());//小部分执行操作
new Runnable(split2.process());
List revisedList = split1 + split2;//将结果合并

3.2.2 Vergleich von sequentiellen und parallelen Leistungstests

Wenn es sich um eine Multi-Core-Maschine handelt, theoretisch paralleler Stream wird besser sein als sequentiell Der Ablauf ist doppelt so schnell. Das Folgende ist der Testcode

long t0 = System.nanoTime();
//初始化一个范围100万整数流,求能被2整除的数字,toArray()是终点方法
int a[]=IntStream.range(0, 1_000_000).filter(p -> p % 2==0).toArray();
long t1 = System.nanoTime();
//和上面功能一样,这里是用并行流来计算
int b[]=IntStream.range(0, 1_000_000).parallel().filter(p -> p % 2==0).toArray();
long t2 = System.nanoTime();
//我本机的结果是serial: 0.06s, parallel 0.02s,证明并行流确实比顺序流快
System.out.printf("serial: %.2fs, parallel %.2fs%n", (t1 - t0) * 1e-9, (t2 - t1) * 1e-9);

3.3 Über das Folk/Join-Framework

Die Parallelität der Anwendungshardware ist in Java 7 verfügbar, das heißt, eine der neuen Funktionen des Pakets java.util.concurrent ist ein paralleles Zerlegungsframework im Fork-Join-Stil, das auch sehr leistungsstark und effizient ist Gehen Sie und recherchieren Sie, ich werde hier nicht auf Details eingehen. Im Vergleich zu Stream.parallel() bevorzuge ich Letzteres.

4. Zusammenfassung

Wenn kein Lambda vorhanden ist, wird die Verwendung von Stream ziemlich umständlich sein Da es sich nicht um eine Standardmethode handelt, wird sich das Sammlungsframework ändern. Dies führt zwangsläufig dazu, dass die JDK-Bibliothek leistungsfähiger und flexibler wird. Die Verbesserungen des Stream- und Sammlungsframeworks sind der beste Beweis.

Weitere neue Java8-Funktionen, die Verwendung von Lambda-Ausdrücken (Verwendungsbeispiele) und verwandte Artikel finden Sie auf der chinesischen PHP-Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn