Voici une autre technique que j'ai couru à travers de l'autre jour:
Collections.nCopies(8, 1)
.stream()
.forEach(i -> System.out.println(i));
L' Collections.nCopies
appel crée un List
contenant n
des copies de la valeur que vous fournissez. Dans ce cas, c'est le coffret Integer
de la valeur 1. Bien sûr, il ne fait pas de créer une liste avec n
éléments; il crée un "virtualisé" liste qui ne contient que la valeur et la longueur, et de tout appel à l' get
au sein de la gamme renvoie simplement la valeur. L' nCopies
méthode a été autour depuis les Collections de Cadre a été présenté chemin du retour dans le JDK 1.2. Bien sûr, la possibilité de créer un flux à partir de son résultat a été ajoutée dans Java SE 8.
Une grosse affaire, une autre façon de faire la même chose à peu près le même nombre de lignes.
Cependant, cette technique est plus rapide que l' IntStream.generate
et IntStream.iterate
approches, et étonnamment, il est également plus rapide que l' IntStream.range
approche.
Pour iterate
et generate
le résultat est peut-être pas surprenant. Le flux de cadre (vraiment, la Spliterators pour ces cours d'eau) est construit sur l'hypothèse que les lambdas seront susceptibles de générer des valeurs différentes à chaque fois, et qu'ils vont générer un nombre illimité de résultats. Cela rend parallèle fractionnement particulièrement difficile. L' iterate
méthode est également problématique pour ce cas, car chaque appel nécessite le résultat de la précédente. De sorte que le flux à l'aide generate
et iterate
ne font pas très bien pour générer répété des constantes.
Le rendement relativement faible de range
est surprenant. Cela aussi est virtualisé, de sorte que les éléments ne fait pas exister dans la mémoire, et la taille est connue à l'avant. Cela devrait rendre rapidement et facilement parallélisables spliterator. Mais, étonnamment, n'a pas très bien. Peut-être la raison en est qu' range
a pour calculer une valeur pour chaque élément de la gamme, puis appeler une fonction sur elle. Mais cette fonction ignore son entrée et retourne une constante, donc je suis surpris de découvrir que ce n'est pas incorporé et tué.
L' Collections.nCopies
technique a faire boxing/unboxing afin de gérer les valeurs, car il n'existe pas de primitive de la spécialisation de l' List
. Puisque la valeur est la même à chaque fois, c'est essentiellement en boîte une fois et que la zone est partagée par tous, n
des copies. Je soupçonne boxing/unboxing est hautement optimisé, même intrinsified, et il peut être incorporé.
Voici le code:
public static final int LIMIT = 500_000_000;
public static final long VALUE = 3L;
public long range() {
return
LongStream.range(0, LIMIT)
.parallel()
.map(i -> VALUE)
.map(i -> i % 73 % 13)
.sum();
}
public long ncopies() {
return
Collections.nCopies(LIMIT, VALUE)
.parallelStream()
.mapToLong(i -> i)
.map(i -> i % 73 % 13)
.sum();
}
Et voici la JMH résultats: (2.8 GHz Core2Duo)
Benchmark Mode Samples Mean Mean error Units
c.s.q.SO18532488.ncopies thrpt 5 7.547 2.904 ops/s
c.s.q.SO18532488.range thrpt 5 0.317 0.064 ops/s
Il y a un juste montant de la variance dans le ncopies version, mais dans l'ensemble il semble confortablement 20x plus vite que la gamme de la version. (Je serais assez disposé à croire que j'ai fait quelque chose de mal, cependant).
Je suis surpris de voir à quel point l' nCopies
technique fonctionne. En interne, il ne font pas grand-chose de spécial, avec le flux de la virtualisé liste tout simplement être mis en œuvre à l'aide de IntStream.range
! J'avais prévu qu'il serait nécessaire de créer une institution spécialisée spliterator pour obtenir cela, pour aller vite, mais il semble déjà être assez bon.