160 votes

Peut-on faire une boucle for each en Java dans l'ordre inverse ?

J'ai besoin de parcourir une liste dans l'ordre inverse en utilisant Java.

Donc là où cela le fait dans le sens avant:

for(String string: stringList){
//...faire quelque chose
}

Y a-t-il un moyen d'itérer sur la stringList dans l'ordre inverse en utilisant la syntaxe for each?

Pour clarifier: je sais comment itérer sur une liste dans l'ordre inverse mais j'aimerais savoir (par curiosité) comment le faire dans le style for each.

7 votes

Le but de la boucle "for-each" est que vous avez juste besoin d'effectuer une opération sur chaque élément, et l'ordre n'est pas important. For-each pourrait traiter les éléments dans un ordre complètement aléatoire, et cela ferait toujours ce pour quoi il a été conçu. Si vous avez besoin de traiter les éléments d'une manière particulière, je vous suggérerais de le faire manuellement.

1 votes

Bibliothèque de collections Java. Pas vraiment lié au langage. Blâmer Josh Bloch.

7 votes

@muusbolla: Mais comme une liste est une collection ordonnée, son ordre sera sûrement respecté, n'est-ce pas ? Par conséquent, le for-each ne traitera pas les éléments d'une liste dans un ordre aléatoire.

157voto

Nat Points 6434

La méthode Collections.reverse retourne en fait une nouvelle liste avec les éléments de la liste d'origine copiés à l'envers, donc cela a une complexité en O(n) par rapport à la taille de la liste d'origine.

Comme solution plus efficace, vous pourriez écrire un décorateur qui présente une vue inversée d'une liste en tant qu'itérable. L'itérateur retourné par votre décorateur utiliserait le ListIterator de la liste décorée pour parcourir les éléments dans l'ordre inverse.

Par exemple:

public class Reversed implements Iterable {
    private final List original;

    public Reversed(List original) {
        this.original = original;
    }

    public Iterator iterator() {
        final ListIterator i = original.listIterator(original.size());

        return new Iterator() {
            public boolean hasNext() { return i.hasPrevious(); }
            public T next() { return i.previous(); }
            public void remove() { i.remove(); }
        };
    }

    public static  Reversed reversed(List original) {
        return new Reversed(original);
    }
}

Et vous l'utiliseriez comme ceci:

import static Reversed.reversed;

...

List someStrings = getSomeStrings();
for (String s : reversed(someStrings)) {
    doSomethingWith(s);
}

23 votes

C'est essentiellement ce que fait Iterables.reverse de Google, oui :)

10 votes

Je sais qu'il y a une "règle" selon laquelle nous devons accepter la réponse de Jon :) mais... Je veux accepter celle-ci (même si elles sont essentiellement les mêmes) car elle ne nécessite pas que j'inclus une autre bibliothèque tierce (même si certains pourraient argumenter que cette raison enfreint l'un des principaux avantages de la POO - la réutilisabilité).

0 votes

Petite erreur : dans public void remove(), il ne devrait pas y avoir d'instruction return, il devrait juste y avoir : i.remove();

101voto

Jon Skeet Points 692016

Pour une liste, vous pourriez utiliser la Bibliothèque Google Guava:

for (String item : Lists.reverse(stringList))
{
    // ...
}

Notez que Lists.reverse ne inverse pas toute la collection, ni ne fait quoi que ce soit de semblable - cela permet simplement l'itération et l'accès aléatoire, dans l'ordre inverse. C'est plus efficace que d'inverser la collection au préalable.

Pour inverser un itérable arbitraire, vous devriez le lire entièrement et ensuite le "rejouer" à l'envers.

(Si vous ne l'utilisez pas déjà, je vous recommande vivement de jeter un œil à Guava. C'est du très bon matériel.)

1 votes

Notre base de code utilise abondamment la version générifiée de Commons Collections publiée par larvalabs (larvalabs.com/collections). En parcourant le dépôt SVN d'Apache Commons, il est clair que la majeure partie du travail pour la sortie d'une version Java 5 de Commons Collections est terminée, ils ne l'ont simplement pas encore publiée.

0 votes

J'aime ça. Si ce n'était pas si utile, j'appellerais ça une prise.

0 votes

Je me demande pourquoi Jakarta n'a jamais pris la peine de mettre à jour Apache Commons.

41voto

Gopi Reddy Points 51

La liste (contrairement à l'ensemble) est une collection ordonnée et son itération préserve l'ordre par contrat. J'aurais attendu à ce qu'une pile itère dans l'ordre inverse mais malheureusement ce n'est pas le cas. Donc la solution la plus simple à laquelle je peux penser est la suivante:

for (int i = stack.size() - 1; i >= 0; i--) {
    System.out.println(stack.get(i));
}

Je réalise que ce n'est pas une solution de boucle "for each". Je préfère utiliser la boucle for plutôt que d'introduire une nouvelle bibliothèque comme Google Collections.

Collections.reverse() fait également le travail mais il met à jour la liste au lieu de retourner une copie dans l'ordre inverse.

3 votes

Cette approche peut être correcte pour les listes basées sur des tableaux (telles que ArrayList) mais elle serait sub-optimale pour les listes chaînées car chaque get devrait traverser la liste du début à la fin (ou éventuellement de la fin au début) pour chaque get. Il est préférable d'utiliser un itérateur plus intelligent comme dans la solution de Nat (optimal pour toutes les implémentations de List).

1 votes

De plus, cela s'écarte de la demande dans le message d'origine qui demande explicitement la syntaxe for each

9voto

Phillip Gibb Points 91

Cela va perturber la liste originale et doit également être appelé en dehors de la boucle. De plus, vous ne voulez pas effectuer une inversion à chaque fois que vous bouclez - cela serait-il vrai si l'une des idées Iterables.reverse était appliquée?

Collections.reverse(stringList);

for(String string: stringList){
//...faire quelque chose
}

5voto

Uri Points 50687

À ma connaissance, il n'y a pas de standard "reverse_iterator" dans la bibliothèque standard qui prend en charge la syntaxe for-each, qui est déjà du sucre syntaxique introduit tardivement dans le langage.

Vous pourriez faire quelque chose comme for(Item element: myList.clone().reverse()) et payer le prix associé.

Cela semble également assez cohérent avec le phénomène apparent de ne pas vous donner de moyens pratiques pour effectuer des opérations coûteuses - puisqu'une liste, par définition, pourrait avoir une complexité d'accès aléatoire O(N) (vous pourriez implémenter l'interface avec un seul lien), l'itération inverse pourrait finir par être de O(N^2). Bien sûr, si vous avez un ArrayList, vous ne payez pas ce prix.

0 votes

Vous pouvez exécuter un ListIterator en arrière, ce qui peut être enveloppé dans un Iterator.

0 votes

@Tom: Bon point. Cependant, avec l'itérateur, vous utilisez toujours les vieilles boucles for ennuyeuses, et vous pourriez encore payer le coût pour accéder au dernier élément pour commencer... J'ai ajouté le qualificatif dans ma réponse, cependant, merci.

0 votes

Deque possède un itérateur inverse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X