Dans de nombreuses autres langues, par exemple. Haskell, il est facile de répéter une valeur ou une fonction plusieurs fois, par exemple. pour obtenir une liste de 8 copies de la valeur 1:
take 8 (repeat 1)
mais je ne l'ai pas encore trouvé dans Java 8. Existe-t-il une telle fonction dans le JDK de Java 8?
Ou bien quelque chose d'équivalent à une gamme comme
[1..8]
Cela semblerait un remplacement évident pour une déclaration verbeuse en Java comme
for (int i = 1; i <= 8; i++) {
System.out.println(i);
}
avoir quelque chose comme
Range.from(1, 8).forEach(i -> System.out.println(i))
bien que cet exemple particulier ne semble pas beaucoup plus concis en fait ... mais j'espère qu'il est plus lisible.
Réponses:
Pour cet exemple spécifique, vous pouvez faire:
Si vous avez besoin d'une étape différente de 1, vous pouvez utiliser une fonction de mappage, par exemple, pour une étape de 2:
Ou créez une itération personnalisée et limitez la taille de l'itération:
la source
IntStream.rangeClosed(1, 8).forEach(i -> methodNoArgs());
) mais cela confond la chose IMO et dans ce cas une boucle semble indiquée.Voici une autre technique que j'ai utilisée l'autre jour:
L'
Collections.nCopies
appel crée une copieList
contenant lan
valeur que vous fournissez. Dans ce cas, c'est laInteger
valeur encadrée 1. Bien sûr, cela ne crée pas réellement une liste avec desn
éléments; il crée une liste «virtualisée» qui ne contient que la valeur et la longueur, et tout appel à l'get
intérieur de la plage renvoie simplement la valeur. LanCopies
méthode existe depuis que le Framework de collections a été introduit 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.Gros problème, une autre façon de faire la même chose dans à peu près le même nombre de lignes.
Cependant, cette technique est plus rapide que l' approche
IntStream.generate
etIntStream.iterate
, et étonnamment, elle est également plus rapide que l'IntStream.range
approche.Car
iterate
etgenerate
le résultat n'est peut-être pas trop surprenant. Le framework de flux (en fait, les Spliterators pour ces flux) est construit sur l'hypothèse que les lambdas généreront potentiellement des valeurs différentes à chaque fois, et qu'ils généreront un nombre illimité de résultats. Cela rend la division parallèle particulièrement difficile. Laiterate
méthode est également problématique dans ce cas car chaque appel nécessite le résultat du précédent. Ainsi, les flux utilisantgenerate
etiterate
ne fonctionnent pas très bien pour générer des constantes répétées.La performance relativement médiocre de
range
est surprenante. Cela aussi est virtualisé, donc les éléments n'existent pas tous en mémoire et la taille est connue à l'avance. Cela devrait conduire à un séparateur rapide et facilement parallélisable. Mais étonnamment, cela n'a pas très bien fonctionné. Peut-être que la raison est qu'ilrange
faut calculer une valeur pour chaque élément de la plage, puis appeler une fonction dessus. Mais cette fonction ignore simplement son entrée et renvoie une constante, donc je suis surpris que cela ne soit pas incorporé et tué.La
Collections.nCopies
technique doit faire du boxing / unboxing afin de gérer les valeurs, car il n'y a pas de spécialisations primitives deList
. Puisque la valeur est la même à chaque fois, elle est essentiellement encadrée une fois et cette boîte est partagée par toutes lesn
copies. Je soupçonne que la boxe / unboxing est hautement optimisée, voire intrinsèque, et qu'elle peut être bien intégrée.Voici le code:
Et voici les résultats JMH: (2.8GHz Core2Duo)
Il y a une bonne quantité de variance dans la version ncopies, mais dans l'ensemble, cela semble confortablement 20 fois plus rapide que la version de gamme. (Je serais tout à fait prêt à croire que j'ai fait quelque chose de mal, cependant.)
Je suis surpris de voir à quel point la
nCopies
technique fonctionne. En interne, cela ne fait pas grand-chose de spécial, le flux de la liste virtualisée étant simplement implémenté en utilisantIntStream.range
! Je m'attendais à ce qu'il soit nécessaire de créer un séparateur spécialisé pour que cela aille rapidement, mais cela semble déjà être assez bon.la source
nCopies
rien ne copie réellement et que les "copies" pointent toutes vers cet objet unique. Il est toujours sûr si cet objet est immuable , comme une primitive encadrée dans cet exemple. Vous faites allusion à cela dans votre déclaration "boxed once", mais il pourrait être intéressant d'appeler explicitement les mises en garde ici car ce comportement n'est pas spécifique à l'auto-boxing.LongStream.range
c'est beaucoup plus lent queIntStream.range
? C'est donc une bonne chose que l'idée de ne pas proposer deIntStream
(mais de l'utiliserLongStream
pour tous les types d'entiers) ait été abandonnée. Notez que pour le cas d'utilisation séquentielle, il n'y a aucune raison d'utiliser stream:Collections.nCopies(8, 1).forEach(i -> System.out.println(i));
fait la même chose,Collections.nCopies(8, 1).stream().forEach(i -> System.out.println(i));
mais peut-être encore plus efficaceCollections.<Runnable>nCopies(8, () -> System.out.println(1)).forEach(Runnable::run);
LongStream.range
effectue pire, car il a deux cartes avec l'LongFunction
intérieur, toutncopies
a trois cartes avecIntFunction
,ToLongFunction
etLongFunction
donc tous les lambdas sont monomorphe. L'exécution de ce test sur un profil de type pré-pollué (qui est plus proche du cas réel) montre qu'ilncopies
est 1,5 fois plus lent.for
boucle ancienne . Bien que votre solution soit plus rapide que leStream
code, je suppose qu'unefor
boucle battrait l'un ou l'autre de ces éléments par une marge significative.Par souci d'exhaustivité, et aussi parce que je ne pouvais pas m'en empêcher :)
La génération d'une séquence limitée de constantes est assez proche de ce que vous verriez dans Haskell, uniquement avec un niveau de verbosité Java.
la source
() -> 1
ne générerait que des 1, est-ce prévu? Donc, la sortie serait1 1 1 1 1 1 1 1
.take 8 (repeat 1)
. les assylies couvraient à peu près tous les autres cas.Stream<T>
a également unegenerate
méthode générique pour obtenir un flux infini d'un autre type, qui peut être limité de la même manière.Une fois qu'une fonction de répétition est quelque part définie comme
Vous pouvez l'utiliser de temps en temps de cette façon, par exemple:
Pour obtenir et équivalent à Haskell
Tu pourrais écrire
la source
Runnable
enFunction<Integer, ?>
puis en utilisantf.apply(i)
.C'est ma solution pour implémenter la fonction times. Je suis un junior donc j'admets que cela pourrait ne pas être idéal, je serais heureux d'apprendre si ce n'est pas une bonne idée pour une raison quelconque.
Voici quelques exemples d'utilisation:
la source