Existe-t-il une opération de flux Java 8 qui limite un (potentiellement infini) Stream
jusqu'à ce que le premier élément ne corresponde pas à un prédicat?
En Java 9, nous pouvons utiliser takeWhile
comme dans l'exemple ci-dessous pour imprimer tous les nombres inférieurs à 10.
IntStream
.iterate(1, n -> n + 1)
.takeWhile(n -> n < 10)
.forEach(System.out::println);
Comme il n'y a pas une telle opération dans Java 8, quelle est la meilleure façon de l'implémenter de manière générale?
java
java-8
java-stream
MForster
la source
la source
IntStream.iterate(1, n->n<10, n->n+1).forEach(System.out::print);
Réponses:
Une telle opération devrait être possible avec un Java 8
Stream
, mais elle ne peut pas nécessairement être effectuée efficacement - par exemple, vous ne pouvez pas nécessairement paralléliser une telle opération, car vous devez regarder les éléments dans l'ordre.L'API ne fournit pas un moyen facile de le faire, mais le moyen le plus simple est probablement de prendre
Stream.iterator()
, d'encapsuler leIterator
pour avoir une implémentation «à prendre en main», puis de revenir à aSpliterator
, puis aStream
. Ou - peut-être - enveloppez leSpliterator
, bien qu'il ne puisse plus vraiment être divisé dans cette implémentation.Voici une implémentation non testée de
takeWhile
sur unSpliterator
:la source
Operations
takeWhile
etdropWhile
ont été ajoutés au JDK 9. Votre exemple de codese comportera exactement comme vous vous y attendez lorsqu'il sera compilé et exécuté sous JDK 9.
Le JDK 9 a été publié. Il est disponible en téléchargement ici: http://jdk.java.net/9/
la source
takeWhile
/dropWhile
: download.java.net/jdk9/docs/api/java/util/stream/Stream.htmltakeWhile
etdropWhile
plutôt quelimitWhile
etskipWhile
, par souci de cohérence avec l'API existante?takeWhile
etdropWhile
sont assez répandus, se produisant dans Scala, Python, Groovy, Ruby, Haskell et Clojure. L'asymétrie avecskip
etlimit
est malheureuse. Peutskip
- être etlimit
aurait dû être appelédrop
ettake
, mais ceux-ci ne sont pas aussi intuitifs à moins que vous ne soyez déjà familier avec Haskell.dropXXX
et cetakeXXX
sont des termes plus populaires, mais je peux personnellement vivre avec les plus SQL-esquelimitXXX
etskipXXX
. Je trouve cette nouvelle asymétrie beaucoup plus déroutante que le choix individuel des termes ... :) (btw: Scala a aussidrop(int)
ettake(int)
)allMatch()
est une fonction de court-circuit, vous pouvez donc l'utiliser pour arrêter le traitement. Le principal inconvénient est que vous devez faire votre test deux fois: une fois pour voir si vous devez le traiter, et encore une fois pour voir s'il faut continuer.la source
Stream.allMatch()
s'agit d'une opération de court-circuit . Donc, cela se terminera même sur un flux infini commeIntStream.iterate()
. Bien sûr, rétrospectivement, il s'agit d'une optimisation sensée.peek
. Si je le rencontrais le mois prochain, je prendrais une minute pour me demander pourquoi le programmeur avant moi a vérifié siallMatch
puis ignoré la réponse.Dans le prolongement de la réponse @StuartMarks . Ma bibliothèque StreamEx a un
takeWhile
fonctionnement compatible avec l'implémentation actuelle du JDK-9. Lors de l'exécution sous JDK-9, il déléguera simplement à l'implémentation JDK (viaMethodHandle.invokeExact
laquelle est vraiment rapide). Lors de l'exécution sous JDK-8, l'implémentation "polyfill" sera utilisée. Donc, en utilisant ma bibliothèque, le problème peut être résolu comme ceci:la source
takeWhile
est l'une des fonctions fournies par la bibliothèque protonpack .la source
Mise à jour: Java 9
Stream
est désormais livré avec une méthode takeWhile .Pas besoin de hacks ou d'autres solutions. Utilisez juste ça!
Je suis sûr que cela peut être grandement amélioré: (quelqu'un pourrait peut-être le rendre thread-safe)
Un hack bien sûr ... Pas élégant - mais ça marche ~: D
la source
Vous pouvez utiliser java8 + rxjava .
la source
En fait, il existe 2 façons de le faire dans Java 8 sans aucune bibliothèque supplémentaire ou en utilisant Java 9.
Si vous souhaitez imprimer les nombres de 2 à 20 sur la console, vous pouvez le faire:
ou
La sortie est dans les deux cas:
Personne n'a encore mentionné anyMatch . C'est la raison de cet article.
la source
Il s'agit de la source copiée à partir du JDK 9 java.util.stream.Stream.takeWhile (Predicate). Une petite différence pour travailler avec JDK 8.
la source
Voici une version réalisée sur ints - comme demandé dans la question.
Usage:
Voici le code pour StreamUtil:
la source
Allez chercher la bibliothèque AbacusUtil . Il fournit l'API exacte que vous souhaitez et plus encore:
Déclaration : Je suis le développeur d'AbacusUtil.
la source
Vous ne pouvez pas abandonner un flux sauf par une opération de terminal en court-circuit, ce qui laisserait certaines valeurs de flux non traitées quelle que soit leur valeur. Mais si vous souhaitez simplement éviter les opérations sur un flux, vous pouvez ajouter une transformation et un filtre au flux:
Cela transforme le flux d'objets en valeurs nulles lorsque les éléments remplissent une condition, puis filtre les valeurs nulles. Si vous êtes prêt à vous adonner aux effets secondaires, vous pouvez définir la valeur de la condition sur true une fois que quelque chose est rencontré, de sorte que toutes les choses suivantes sont filtrées quelle que soit leur valeur. Mais même si ce n'est pas le cas, vous pouvez économiser beaucoup (sinon la totalité) du traitement en filtrant les valeurs du flux que vous ne souhaitez pas traiter.
la source
Même moi, j'avais une exigence similaire: invoquez le service Web, s'il échoue, réessayez 3 fois. S'il échoue même après ces nombreux essais, envoyez une notification par e-mail. Après avoir beaucoup cherché sur Google,
anyMatch()
est venu en sauveur. Mon exemple de code comme suit. Dans l'exemple suivant, si la méthode webServiceCall renvoie true dans la première itération elle-même, stream n'itère pas plus loin comme nous l'avons appeléanyMatch()
. Je crois que c'est ce que vous recherchez.la source
Si vous connaissez le nombre exact de répétitions qui seront effectuées, vous pouvez faire
la source
au lieu de peak, vous pouvez utiliser mapToObj pour renvoyer l'objet ou le message final
la source
Si vous avez un problème différent, une solution différente peut être nécessaire, mais pour votre problème actuel, j'irais simplement avec:
la source
Peut-être un peu hors sujet, mais c'est ce que nous avons pour
List<T>
plutôt queStream<T>
.Vous devez d'abord avoir une
take
méthode util. Cette méthode prend les premiersn
éléments:ça marche juste comme
scala.List.take
maintenant, il sera assez simple d'écrire une
takeWhile
méthode basée surtake
ça marche comme ça:
cette implémentation itère partiellement la liste plusieurs fois mais n'ajoute pas d'
O(n^2)
opérations d' ajout . J'espère que c'est acceptable.la source
J'ai une autre solution rapide en mettant en œuvre ceci (ce qui est plutôt impur en fait, mais vous voyez l'idée):
la source
current
jamais.equals(e)
, vous obtiendrez une boucle sans fin. Les deux même si vous postulez par exemple.limit(1)
. C'est bien pire que «impur» .Voici ma tentative en utilisant uniquement la bibliothèque Java Stream.
la source
filter
prédicat est censé être apatride.System.out.println
est un effet secondaire.