Je vois souvent du code comme celui-ci:
employees.stream().map(e -> e.getName()).collect(Collectors.toList());
Code qui crée une liste avec tous les éléments de la liste nommés «employés» traduits en noms. C'est un code normal, mais comme le type lambda est comme une classe interne, n'y a-t-il pas une surcharge? Si vous appelez juste un tel getter
employees.stream().map(Employee::getName).collect(Collectors.toList());
N'est-ce pas plus rapide?
J'ai pensé, je l'ai mesuré pour m'en assurer. (Bien qu'il soit un peu vieux, il a été mesuré avec Oracle JDK 1.8u131.)
En utilisant JMH, j'ai mesuré le nombre d'exécutions (débit) de chaque code par seconde.
public class PerformanceMethods {
/**Générer 10000 éléments*/
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());
}
}
Le résultat est ···
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
Plus le score est élevé, plus la vitesse de traitement est rapide, mais le résultat est presque le même. Si vous l'exécutez à plusieurs reprises, peu importe celui que vous utilisez.
En passant, à titre de référence, je publierai un document décrivant le mécanisme d'exécution des expressions lambda. https://www.slideshare.net/miyakawataku/lambda-meets-invokedynamic
Normalement, lorsque vous créez une classe interne, un fichier de classe est généré au moment de la compilation (classe avec "$"), mais les expressions lambda sont initialisées au moment de l'exécution, il semble donc démarrer plus rapidement. Au contraire, seul le premier moment de l'exécution de l'expression lambda sera retardé du montant de l'initialisation.
Je ne pense pas que cela me dérange, mais personnellement, j'aimerais utiliser une référence de méthode propre.
Lorsque j'ai examiné le code créé par d'autres, j'ai vu ce code.
employees.stream().map(Employee::getAddress).map(Address::getPostalCode).collect(Collectors.toList());
Pour la première fois, j'ai vu comment utiliser map
deux fois pour convertir lors de la création d'une liste de valeurs pour une entité imbriquée. ..
Bien sûr, j'ai écrit que "je veux personnellement utiliser une référence de méthode propre", mais il semble que map
deux fois est inefficace ...?
Donc, j'ai mesuré les performances lors de l'écriture de map
une fois avec une expression lambda et lors de l'écriture de map
deux fois avec la référence de méthode.
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());
}
}
Le résultat est ···
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
Je suis curieux que la plage d'erreur (variation du temps de traitement) soit plus grande pour l'expression lambda (mapOnce
), mais j'ai trouvé qu'une fois map
est encore plus rapide.
map
Il est plus facile à comprendre si vous écrivez une fois l'expression lambda. ..
--Il n'y a pratiquement aucune différence de vitesse entre la référence de méthode et l'expression lambda. Cependant, puisque l'expression lambda est initialisée au moment de l'exécution, il y a une petite surcharge seulement au début.
map
une fois en utilisant une expression lambda que de prendre une valeur en utilisant une référence de méthode deux fois. Et facile à lire.Recommended Posts