Question générale: Quelle est la bonne façon d'inverser un flux? En supposant que nous ne sachions pas de quel type d'éléments ce flux se compose, quel est le moyen générique d'inverser un flux?
Question spécifique:
IntStream
fournit une méthode de plage pour générer des entiers dans une plage spécifique IntStream.range(-range, 0)
, maintenant que je veux l'inverser, la commutation de la plage de 0 à négative ne fonctionnera pas, je ne peux pas non plus utiliserInteger::compare
List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().sorted(Integer::compare).forEach(System.out::println);
avec IntStream
j'obtiendrai cette erreur du compilateur
Erreur: (191, 0) ajc: La méthode
sorted()
du typeIntStream
n'est pas applicable pour les arguments (Integer::compare
)
Qu'est-ce que j'oublie ici?
IntStream
n'a pas de.sorted(Comparator)
méthode; vous devez passer par unStream<Integer>
premier et inverser là-bas avant de céder unIntStream
IntStream.range(0, n)
dans l'ordre inverse, faites quelque chose commemap(i -> n - i - 1)
. Pas besoin de faire de la boxe et du tri.1, 3, 2
, quel est votre résultat attendu? Voulez-vous le flux inversé2, 3, 1
ou le flux trié comme3, 2, 1
?Réponses:
Pour la question spécifique de la génération d'un reverse
IntStream
, essayez quelque chose comme ceci:Cela évite la mise en boîte et le tri.
Pour la question générale de savoir comment inverser un flux de n'importe quel type, je ne sais pas s'il existe un moyen "approprié". Je peux penser à plusieurs façons. Les deux finissent par stocker les éléments du flux. Je ne connais pas de moyen d'inverser un flux sans stocker les éléments.
Cette première méthode stocke les éléments dans un tableau et les lit dans un flux dans l'ordre inverse. Notez que puisque nous ne connaissons pas le type d'exécution des éléments de flux, nous ne pouvons pas taper le tableau correctement, ce qui nécessite un cast non vérifié.
Une autre technique utilise des collectionneurs pour accumuler les éléments dans une liste inversée. Cela fait beaucoup d'insertions à l'avant des
ArrayList
objets, donc il y a beaucoup de copie en cours.Il est probablement possible d'écrire un collecteur inverseur beaucoup plus efficace en utilisant une sorte de structure de données personnalisée.
MISE À JOUR 29/01/2016
Étant donné que cette question a attiré un peu d'attention récemment, je pense que je devrais mettre à jour ma réponse pour résoudre le problème d'insertion au début de
ArrayList
. Ce sera horriblement inefficace avec un grand nombre d'éléments, nécessitant une copie O (N ^ 2).Il est préférable d'utiliser un à la
ArrayDeque
place, qui prend en charge efficacement l'insertion à l'avant. Une petite ride est que nous ne pouvons pas utiliser la forme à trois arguments deStream.collect()
; cela nécessite que le contenu du second argument soit fusionné dans le premier argument, et il n'y a pas d'opération en bloc "ajouter tout à l'avant"Deque
. Au lieu de cela, nous utilisonsaddAll()
pour ajouter le contenu du premier argument à la fin du second, puis nous renvoyons le second. Cela nécessite l'utilisation de laCollector.of()
méthode d'usine.Le code complet est le suivant:
Le résultat est un
Deque
au lieu d'unList
, mais cela ne devrait pas être un gros problème, car il peut facilement être itéré ou diffusé dans l'ordre maintenant inversé.la source
IntStream.iterate(to-1, i->i-1).limit(to-from)
.limit(endExcl-(long)startIncl)
place, mais pour des flux aussi importants, c'est quand même très déconseillé car c'est beaucoup moins efficace que larange
solution basée. Au moment où j'ai écrit le commentaire, je n'étais pas au courant de la différence d'efficacité.Solution élégante
la source
Comparable
...Beaucoup de solutions ici trient ou inversent le
IntStream
, mais cela nécessite inutilement un stockage intermédiaire. La solution de Stuart Marks est la voie à suivre:Il gère également correctement le débordement, en passant ce test:
la source
Estreams
nom (je vais le supprimer du message). C'est l'une des classes d'utilité internes de notre société, que nous utilisons pour compléterjava.util.stream.Stream
lesstatic
méthodes de.StreamEx
spécifiant l'étape:IntStreamEx.rangeClosed(from-1, to, -1)
Question générale:
Stream ne stocke aucun élément.
Il n'est donc pas possible d'itérer les éléments dans l'ordre inverse sans stocker les éléments dans une collection intermédiaire.
Mise à jour: LinkedList changé en ArrayDeque (mieux) voir ici pour plus de détails
Impressions:
À propos, l'utilisation de la
sort
méthode n'est pas correcte car elle trie, PAS inverse (en supposant que le flux peut avoir des éléments non ordonnés)Question spécifique:
J'ai trouvé cela simple, plus facile et intuitif ( commentaire copié sur @Holger )
la source
sorted
etdistinct
stockent en fait un résultat intermédiaire. Consultez la documentation de l'API du package pour obtenir des informations à ce sujet.No storage
dans la même page. Même s'il stocke, nous ne pouvons pas avoir accès à ce stockage (doncNo storage
c'est bien je suppose)sans lib externe ...
la source
Si elles sont appliquées
Comparable<T>
(ex.Integer
,String
,Date
), Vous pouvez le faire en utilisantComparator.reverseOrder()
.la source
Stream.of(1,3,2)
le résultat ne seraitStream.of(3,2,1)
PASStream.of(2,3,1)
Vous pouvez définir votre propre collecteur qui collecte les éléments dans l'ordre inverse:
Et utilisez-le comme:
J'utilise une ArrayList pour insérer efficacement les éléments collectés (à la fin de la liste) et Guava Lists.reverse pour donner efficacement une vue inversée de la liste sans en faire une autre copie.
Voici quelques cas de test pour le collecteur personnalisé:
la source
cyclops- react StreamUtils a une méthode de flux inversé ( javadoc ).
Il fonctionne en collectant dans un ArrayList, puis en utilisant la classe ListIterator qui peut itérer dans les deux sens, pour parcourir la liste en arrière.
Si vous avez déjà une liste, ce sera plus efficace
la source
Je suggérerais d'utiliser jOOλ , c'est une excellente bibliothèque qui ajoute de nombreuses fonctionnalités utiles aux flux et lambdas Java 8.
Vous pouvez alors effectuer les opérations suivantes:
Aussi simple que cela. C'est une bibliothèque assez légère, qui vaut la peine d'être ajoutée à n'importe quel projet Java 8.
la source
Voici la solution que j'ai trouvée:
puis en utilisant ces comparateurs:
la source
Collections.reverseOrder()
existe depuis Java 1.2 et fonctionne avecInteger
…Et cette méthode utilitaire?
Semble fonctionner avec tous les cas sans duplication.
la source
la source
Méthode la plus simple (collecte simple - prend en charge les flux parallèles):
Méthode avancée (prend en charge les flux parallèles de manière continue):
Notez que vous pouvez rapidement étendre à d'autres types de flux (IntStream, ...).
Essai:
Résultats:
Notes complémentaires: Le
simplest way
il n'est pas si utile lorsqu'il est utilisé avec d' autres opérations de flux (la jointure Collect rompt le parallélisme). Leadvance way
n'a pas ce problème, et il conserve également les caractéristiques initiales du flux, par exempleSORTED
, et donc, c'est la voie à suivre pour l'utiliser avec d'autres opérations de flux après l'inverse.la source
On pourrait écrire un collecteur qui collecte les éléments dans l'ordre inverse:
Et utilisez-le comme ceci:
Réponse originale (contient un bogue - cela ne fonctionne pas correctement pour les flux parallèles):
Une méthode d'inversion de flux à usage général pourrait ressembler à:
la source
Pas purement Java8 mais si vous utilisez la méthode Lists.reverse () de goyave en conjonction, vous pouvez facilement y parvenir:
la source
Concernant la question spécifique de la génération d'un reverse
IntStream
:à partir de Java 9, vous pouvez utiliser la version à trois arguments de
IntStream.iterate(...)
:où:
IntStream.iterate(int seed, IntPredicate hasNext, IntUnaryOperator next);
seed
- l'élément initial;hasNext
- un prédicat à appliquer aux éléments pour déterminer quand le flux doit se terminer;next
- une fonction à appliquer à l'élément précédent pour produire un nouvel élément.la source
Pour référence, je regardais le même problème, je voulais joindre la valeur de chaîne des éléments de flux dans l'ordre inverse.
itemList = {dernier, milieu, premier} => premier, milieu, dernier
J'ai commencé à utiliser une collection intermédiaire avec
collectingAndThen
de comonad ou leArrayDeque
collectionneur de Stuart Marks , même si je n'étais pas satisfait de la collection intermédiaire, et du streaming à nouveauJ'ai donc répété la réponse de Stuart Marks qui utilisait l'
Collector.of
usine, qui a l'intéressant finisseur lambda.Puisque dans ce cas, le flux n'est pas parallèle, le combineur n'est pas tellement pertinent, je l'utilise
insert
quand même pour des raisons de cohérence du code mais cela n'a pas d'importance car cela dépendrait du constructeur de chaînes construit en premier.J'ai regardé le StringJoiner, mais il n'a pas de
insert
méthode.la source
Répondre à la question spécifique de l'inversion avec IntStream, ci-dessous a fonctionné pour moi:
la source
ArrayDeque
sont plus rapides dans la pile qu'une pile ou une LinkedList. "push ()" insère des éléments à l'avant du Dequela source
Chaîne inversée ou n'importe quel tableau
la division peut être modifiée en fonction du délimiteur ou de l'espace
la source
la solution la plus simple consiste à utiliser
List::listIterator
etStream::generate
la source
Stream.generate()
génère en flux infini, donc l'appel àlimit()
est très important ici.Voilà comment je fais.
Je n'aime pas l'idée de créer une nouvelle collection et de l'inverser.
L'idée de la carte IntStream # est assez soignée, mais je préfère la méthode IntStream # iterate, car je pense que l'idée d'un compte à rebours jusqu'à zéro mieux exprimée avec la méthode iterate et plus facile à comprendre en termes de marche du tableau de l'arrière vers l'avant.
Voici quelques tests pour prouver que cela fonctionne:
la source
Dans tout cela, je ne vois pas la réponse à laquelle j'irais en premier.
Ce n'est pas exactement une réponse directe à la question, mais c'est une solution potentielle au problème.
Construisez simplement la liste à l'envers en premier lieu. Si vous le pouvez, utilisez une LinkedList au lieu d'une ArrayList et lorsque vous ajoutez des éléments, utilisez "Push" au lieu d'ajouter. La liste sera construite dans l'ordre inverse et sera ensuite diffusée correctement sans aucune manipulation.
Cela ne convient pas aux cas où vous avez affaire à des tableaux ou des listes primitifs qui sont déjà utilisés de différentes manières, mais qui fonctionnent bien dans un nombre surprenant de cas.
la source
Cette méthode fonctionne avec n'importe quel flux et est compatible Java 8:
la source
Le moyen le plus générique et le plus simple d'inverser une liste sera:
la source
Comparator
. En conséquence, personne ne peut vous garantir que cette "astuce" fonctionnera dans n'importe quelle future version de Java avec n'importe quel algorithme de tri. La même astuce ne fonctionne pas pour le flux parallèle, par exemple, car l'algorithme de tri parallèle l'utiliseComparator
de manière différente. Pour le tri séquentiel, cela fonctionne uniquement par hasard. Je ne recommanderais à personne d'utiliser cette solution.System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))
(le résultat peut toutefois dépendre du nombre de cœurs).Java 8 façon de faire cela:
la source