Le moyen le plus efficace pour obtenir le dernier élément d'un flux
Stream ne pas avoir un last()
méthode:
Stream<T> stream;
T last = stream.last(); //No such method
Quelle est la plus élégante et/ou un moyen efficace d'obtenir le dernier élément (ou null pour un vide de Flux)?
- Si vous avez besoin de trouver le dernier élément d'un
Stream
, vous pouvez vouloir reconsidérer votre conception, et si vous voulez vraiment être à l'aide d'unStream
.Stream
s ne sont pas nécessairement ordonné ou finis. Si votreStream
est pas ordonné, à l'infini, ou les deux, le dernier élément n'a pas de sens. Dans mon esprit, le point d'uneStream
est de fournir une couche d'abstraction entre les données et comment vous devez traiter. En tant que tel, unStream
lui-même n'a pas besoin de savoir quelque chose au sujet de l'ordre relatif des éléments. Trouver le dernier élément dans unStream
est O(n). Si vous aviez une autre structure de données, il pourrait être O(1). - le besoin était réel: la situation était à peu près à ajouter des articles à votre panier, chaque ajout d'erreur renvoyé info (certaines combinaisons d'éléments n'étaient pas valables), mais seulement la dernière addition d'erreur d'info (lorsque tous les points ont été ajoutés et une juste évaluation de la charrette qui pourrait être fait) était l'info nécessaire. (Oui, l'API que nous utilisons est brisé et ne peut pas être fixé).
- Les flux infinis n'ont pas bien défini
count()
soit, mais Stream a encore uncount()
méthode. Vraiment, cet argument s'applique à tous les non-court-circuit terminal de l'opération sur les flux infinis. - Je pense que les diffusions ont
last()
méthode. Il pourrait y avoir une enquête, le 1er avril, la façon dont il doit être défini pour les flux infinis. Je propose: "Il ne revient jamais et il utilise au moins un processeur core à 100%. Sur les ruisseaux parallèles, il est nécessaire d'utiliser tous les coeurs à 100%."
Vous devez vous connecter pour publier un commentaire.
Faire une réduction qui retourne simplement la valeur actuelle:
O(n)
, même si, divisé par le nombre de cœurs du PROCESSEUR. Depuis le stream ne sais pas ce que la fonction de réduction de fait, il reste à évaluer pour chaque élément.Cela dépend fortement de la nature de la
Stream
. Gardez à l'esprit que la “simple” ne veut pas forcément dire “efficace”. Si vous pensez que le flux très importants, le transport de lourdes opérations ou d'avoir une source qui connaît la taille à l'avance, les éléments suivants pourraient être sensiblement plus efficace que la solution la plus simple:Vous peut illustrer la différence avec l'exemple suivant:
Il apparaîtra à l'impression:
En d'autres termes, il n'a pas effectuer l'opération sur la première 9999999 éléments, mais seulement sur la dernière.
hasCharacteristics()
bloc? Quelle est la valeur qu'il ajoute ce n'est pas déjà couverte par lerecursive()
méthode? Ce dernier a déjà navigue vers le dernier point de split. En outre,recursive()
ne peut pas revenirnull
de sorte que vous pouvez supprimer leit != null
vérifier.SUBSIZED
ruisseau qui garantit la non-vide fractionné moitiés, nous n'avons jamais besoin de revenir sur le côté gauche. Notez que dans ce casrecursive
ne sera pas réellement de manière récursive comme letrySplit
a déjà fait ses preuves en retournull
.null
-vérifier les tiges à partir d'une version antérieure, mais ensuite j'ai découvert que pour les non-SUBSIZED
flux que vous avez à faire face à d'éventuelles vide split parties, c'est à dire que vous avez à itérer pour savoir si il a des valeurs, donc j'ai déplacé leSpliterators.iterator(…)
appel dans unrecursive
méthode pour être en mesure de sauvegarder sur le côté gauche si la droite est vide. La boucle est toujours le préféré de l'opération.parallel()
que cela peut effectivement effectuer certaines opérations (comme le tri) en parallèle de façon inattendue en consommant plus de cœurs..parallel()
, mais en effet, il peut avoir un effet sursorted()
oudistinct()
. Je ne pense pas qu'il devrait y avoir un effet de tous les autres intermédiaires en opérations de...IntStream.range(0, 10_000_000).filter(x -> x > 0).mapToObj(i -> {...
, c'est à dire: en ajoutant quefilter
, mais en enlevant lesparallel
, laSpliterator
est considérée comme non-splitable, ne sais pas pourquoi c'est comme ça siSpliterator
de mise en œuvre et l'une revient à partir d'un flux séquentiel ne prend pas en charge le fractionnement, bien que je ne peux pas imaginer que cela a un avantage significatif pour les cours d'eau composé de apatrides opérations seulement.C'est juste un refactoring de Holger's réponse parce que le code, tandis que le fantastique, c'est un peu difficile à lire ou à comprendre, surtout pour les personnes qui n'ont pas été programmeurs C avant de Java. J'espère que mon remaniée exemple de classe est un peu plus facile à suivre pour ceux qui ne sont pas familiers avec spliterators, ce qu'ils font ou comment ils fonctionnent.
Goyave a Les ruisseaux.findLast:
reduce((a, b) -> b)
car il utiliseSpliterator.trySplit
en interneVoici une autre solution (pas très efficace):
substream
méthode, et même si cela ne fonctionne pas carcount
est un terminal de l'opération. Alors, quelle est l'histoire derrière tout cela?count==0
d'abord commeStream.skip
n'aime pas-1
comme entrée. En outre, la question n'est pas de dire que vous pouvez acquérir à l'Stream
deux fois. Ni at-il dire que l'acquisition de laStream
deux fois, c'est la garantie d'obtenir le même nombre d'éléments.Parallèle sans apprêt flux avec "skip" méthodes sont difficiles et l' @Holger la mise en œuvre donne une mauvaise réponse. Aussi @Holger la mise en œuvre est un peu plus lent, car il utilise des itérateurs.
Une optimisation de @Holger réponse:
Tests unitaires en utilisant junit 5:
La seule solution pour prendre en charge les deux scénarios est d'éviter la détection de la dernière spliterator sans apprêt sur des thèmes parallèles. La conséquence est que la solution pourra effectuer des opérations sur tous les éléments, mais il vous donnera toujours la bonne réponse.
Noter que dans séquentielle des ruisseaux, il sera de toute façon effectuer des opérations sur tous les éléments.
À l'égard de l'unité de test pour la mise en œuvre, le premier des trois tests sont exactement les mêmes (séquentiel & taille parallèle). Les tests de non dimensionnés parallèle êtes ici:
StreamSupport.stream(((Iterable<Long>) stream::iterator).spliterator(), stream.isParallel())
, en passant par unIterable
détour qui n'a pas de caractéristiques à tous, en d'autres termes crée un non ordonnée stream. Ainsi, le résultat n'a rien à voir avec parallèle ou en utilisantskip
, mais juste avec le fait que la “dernière” n'a pas de sens pour un non ordonnée des flux, de sorte que tout élément est un résultat valide.