Ich sehe oft Code wie diesen:
employees.stream().map(e -> e.getName()).collect(Collectors.toList());
Code, der eine Liste mit allen Elementen der Liste "Mitarbeiter" erstellt, die in Namen übersetzt werden. Es ist ein normaler Code, aber da der Lambda-Typ wie eine innere Klasse ist, gibt es keinen Overhead? Wenn Sie nur so einen Getter nennen
employees.stream().map(Employee::getName).collect(Collectors.toList());
Ist es nicht schneller?
Ich dachte, ich habe es gemessen, um sicherzugehen. (Obwohl es etwas alt ist, wurde es mit Oracle JDK 1.8u131 gemessen.)
Mit JMH habe ich die Anzahl der Ausführungen (Durchsatz) jedes Codes pro Sekunde gemessen.
public class PerformanceMethods {
/**Generieren Sie 10000 Elemente*/
private static final List<Employee> employees = IntStream.range(0, 10000)
.mapToObj(i -> new Employee(i, "name" + i))
.collect(Collectors.toList());
@Benchmark
public void useReference() {
employees.stream().map(Employee::getName).collect(Collectors.toList());
}
@Benchmark
public void useLambda() {
employees.stream().map(e -> e.getName()).collect(Collectors.toList());
}
}
Ergebnis ist ···
Benchmark Mode Cnt Score Error Units
PerformanceMethods.useLambda thrpt 5 5842.820 ± 65.662 ops/s
PerformanceMethods.useReference thrpt 5 5762.353 ± 343.302 ops/s
Je höher der Score, desto schneller die Verarbeitungsgeschwindigkeit, aber das Ergebnis ist fast das gleiche. Wenn Sie es wiederholt ausführen, spielt es keine Rolle, welches Sie verwenden.
Übrigens werde ich als Referenz ein Dokument veröffentlichen, das den Mechanismus der Ausführung von Lambda-Ausdrücken beschreibt. https://www.slideshare.net/miyakawataku/lambda-meets-invokedynamic
Normalerweise generiert das Erstellen einer inneren Klasse zur Kompilierungszeit eine Klassendatei (Klasse mit "$"), aber Lambda-Ausdrücke werden zur Laufzeit initialisiert, sodass sie schneller zu starten scheint. Im Gegenteil, nur der erste Zeitpunkt, zu dem der Lambda-Ausdruck ausgeführt wird, wird um den Initialisierungsbetrag verzögert.
Ich glaube nicht, dass es mich stört, aber ich persönlich würde gerne eine saubere Methodenreferenz verwenden.
Als ich den von anderen erstellten Code überprüfte, sah ich diesen Code.
employees.stream().map(Employee::getAddress).map(Address::getPostalCode).collect(Collectors.toList());
Zum ersten Mal habe ich gesehen, wie man beim Erstellen einer Werteliste für verschachtelte Entitäten zweimal "map" zum Konvertieren verwendet. ..
Sicher, ich schrieb, dass "ich persönlich eine saubere Methodenreferenz verwenden möchte", aber es scheint, dass "map" zweimal ineffizient ist ...?
Also habe ich die Leistung gemessen, wenn ich "map" einmal mit einem Lambda-Ausdruck und zweimal "map" mit der Methodenreferenz geschrieben habe.
public class PerformanceMethods {
private static final List<Employee> employees = IntStream.range(0, 10000)
.mapToObj(i -> new Employee(i, "name" + i, new Address("code" + i)))
.collect(Collectors.toList());
@Benchmark
public void mapOnce() {
employees.stream()
.map(e -> e.getAddress().getCode())
.collect(Collectors.toList());
}
@Benchmark
public void mapTwice() {
employees.stream()
.map(Employee::getAddress)
.map(Address::getCode)
.collect(Collectors.toList());
}
}
Ergebnis ist ···
Benchmark Mode Cnt Score Error Units
PerformanceMethods.mapOnce thrpt 5 6340.454 ± 1291.055 ops/s
PerformanceMethods.mapTwice thrpt 5 5487.546 ± 488.373 ops/s
Ich bin neugierig, dass der Fehlerbereich (Variation der Verarbeitungszeit) für den Lambda-Ausdruck (mapOnce
) größer ist, aber ich habe festgestellt, dass map
einmal noch schneller ist.
map
Es ist einfacher zu verstehen, wenn Sie den Lambda-Ausdruck einmal schreiben. ..
Recommended Posts