Parfois, vous souhaitez filtrer un Stream
avec plusieurs conditions:
myList.stream().filter(x -> x.size() > 10).filter(x -> x.isCool()) ...
ou vous pouvez faire de même avec une condition complexe et une seule filter
:
myList.stream().filter(x -> x.size() > 10 && x -> x.isCool()) ...
Je suppose que la deuxième approche a de meilleures caractéristiques de performance, mais je ne le sais pas.
La première approche gagne en lisibilité, mais quoi de mieux pour les performances?
Réponses:
Le code qui doit être exécuté pour les deux alternatives est si similaire que vous ne pouvez pas prédire un résultat de manière fiable. La structure d'objet sous-jacente peut différer, mais cela ne représente pas un défi pour l'optimiseur de hotspot. Cela dépend donc d'autres conditions environnantes qui se traduiront par une exécution plus rapide, s'il y a une différence.
La combinaison de deux instances de filtre crée plus d'objets et donc plus de code de délégation, mais cela peut changer si vous utilisez des références de méthode plutôt que des expressions lambda, par exemple, remplacez
filter(x -> x.isCool())
parfilter(ItemType::isCool)
. De cette façon, vous avez éliminé la méthode de délégation synthétique créée pour votre expression lambda. Ainsi, la combinaison de deux filtres à l'aide de deux références de méthode peut créer le même code de délégation ou moins qu'unfilter
appel unique utilisant une expression lambda avec&&
.Mais, comme indiqué, ce type de surcharge sera éliminé par l'optimiseur HotSpot et est négligeable.
En théorie, deux filtres pourraient être plus faciles à paralléliser qu'un seul filtre, mais cela n'est pertinent que pour des tâches intenses plutôt informatiques¹.
Il n'y a donc pas de réponse simple.
En fin de compte, ne pensez pas à ces différences de performances en dessous du seuil de détection des odeurs. Utilisez ce qui est plus lisible.
¹… et nécessiterait une implémentation faisant un traitement parallèle des étapes suivantes, une route actuellement non empruntée par l'implémentation Stream standard
la source
Une condition de filtre complexe est meilleure du point de vue des performances, mais les meilleures performances montreront l'ancienne mode pour la boucle avec une norme
if clause
est la meilleure option. La différence sur un petit tableau de 10 éléments peut être ~ 2 fois, pour un grand tableau, la différence n'est pas si grande.Vous pouvez jeter un œil à mon projet GitHub , où j'ai fait des tests de performances pour plusieurs options d'itération de tableau
Pour les ops / s de débit à 10 éléments de petite baie: Pour les ops / s de débit moyen à 10 000 éléments: débit à 1 000 000 d'éléments pour grande baie:
REMARQUE: les tests s'exécutent sur
METTRE À JOUR: Java 11 a quelques progrès sur les performances, mais la dynamique reste la même
Mode de référence: débit, opérations / temps
la source
Ce test montre que votre deuxième option peut être beaucoup plus performante. Les résultats d'abord, puis le code:
maintenant le code:
la source
Test #1: {count=100, sum=7207, min=65, average=72.070000, max=91} Test #3: {count=100, sum=7959, min=72, average=79.590000, max=97} Test #2: {count=100, sum=8869, min=79, average=88.690000, max=110}
C'est le résultat des 6 combinaisons différentes de l'échantillon test partagé par @Hank D Il est évident que le prédicat de la forme
u -> exp1 && exp2
est très performant dans tous les cas.la source