web-dev-qa-db-fra.com

Itérer dans une liste dans l'ordre inverse dans java

Je migre un morceau de code pour utiliser des génériques. Un argument en ce sens est que la boucle for est beaucoup plus propre que le suivi des index ou l'utilisation d'un itérateur explicite.

Dans environ la moitié des cas, la liste (un ArrayList) est itérée dans l'ordre inverse en utilisant un index aujourd'hui.

Quelqu'un peut-il suggérer une méthode plus propre pour le faire (puisque je n'aime pas le indexed for loop lorsque je travaille avec des collections), bien que cela fonctionne?

 for (int i = nodes.size() - 1; i >= 0; i--) {
    final Node each = (Node) nodes.get(i);
    ...
 }

Note: Je ne peux ajouter aucune nouvelle dépendance en dehors du JDK.

234
Allain Lalonde

Essaye ça:

// Substitute appropriate type.
ArrayList<...> a = new ArrayList<...>();

// Add elements to list.

// Generate an iterator. Start just after the last element.
ListIterator li = a.listIterator(a.size());

// Iterate in reverse.
while(li.hasPrevious()) {
  System.out.println(li.previous());
}
432
John Feminella

Guava offre Lists#reverse(List) et ImmutableList#reverse() . Comme dans la plupart des cas pour Guava, le premier délègue à ce dernier si l'argument est un ImmutableList, vous pouvez donc utiliser le premier dans tous les cas. Ceux-ci ne créent pas de nouvelles copies de la liste mais juste des "vues inversées" de celle-ci.

Exemple

List reversed = ImmutableList.copyOf(myList).reverse();
34
Geoffrey Zheng

Je ne pense pas qu'il soit possible d'utiliser la syntaxe de la boucle for. La seule chose que je peux suggérer est de faire quelque chose comme:

Collections.reverse(list);
for (Object o : list) {
  ...
}

... mais je ne dirais pas que c'est "plus propre" étant donné que ça va être moins efficace.

23
Adamski

Option 1: Avez-vous pensé à inverser la liste avec Collections # reverse () et à utiliser ensuite foreach?

Bien sûr, vous pouvez également vouloir refactoriser votre code de telle sorte que la liste soit ordonnée correctement, de sorte que vous n'ayez pas à l'inverser, qui utilise un espace/temps supplémentaire.


MODIFIER:

Option 2: Sinon, pourriez-vous utiliser un Deque au lieu d'un ArrayList? Cela vous permettra d'itérer en avant et en arrière


MODIFIER:

Option 3: comme d'autres l'ont suggéré, vous pouvez écrire un Iterator qui parcourt la liste à l'envers, voici un exemple:

import Java.util.Iterator;
import Java.util.List;

public class ReverseIterator<T> implements Iterator<T>, Iterable<T> {

    private final List<T> list;
    private int position;

    public ReverseIterator(List<T> list) {
        this.list = list;
        this.position = list.size() - 1;
    }

    @Override
    public Iterator<T> iterator() {
        return this;
    }

    @Override
    public boolean hasNext() {
        return position >= 0;
    }

    @Override
    public T next() {
        return list.get(position--);
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException();
    }

}


List<String> list = new ArrayList<String>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");

for (String s : new ReverseIterator<String>(list)) {
    System.out.println(s);
}
14
Kevin

Vous pouvez utiliser la classe concrète LinkedList au lieu de l'interface générale List. Ensuite, vous avez un descendingIterator pour effectuer une itération en sens inverse.

LinkedList<String > linkedList;
for( Iterator<String > it = linkedList.descendingIterator(); it.hasNext(); ) {
    String text = it.next();
}

Je ne sais pas pourquoi il n'y a pas descendingIterator avec ArrayList...

9
tangens

C'est une vieille question, mais il manque une réponse compatible Java8. Voici quelques façons de procéder à une itération inverse de la liste, à l'aide de l'API Streaming:

List<Integer> list = new ArrayList<Integer>(Arrays.asList(1, 3, 3, 7, 5));
list.stream().forEach(System.out::println); // 1 3 3 7 5

int size = list.size();

ListIterator<Integer> it = list.listIterator(size);
Stream.generate(it::previous).limit(size)
    .forEach(System.out::println); // 5 7 3 3 1

ListIterator<Integer> it2 = list.listIterator(size);
Stream.iterate(it2.previous(), i -> it2.previous()).limit(size)
    .forEach(System.out::println); // 5 7 3 3 1

// If list is RandomAccess (i.e. an ArrayList)
IntStream.range(0, size).map(i -> size - i - 1).map(list::get)
    .forEach(System.out::println); // 5 7 3 3 1

// If list is RandomAccess (i.e. an ArrayList), less efficient due to sorting
IntStream.range(0, size).boxed().sorted(Comparator.reverseOrder())
    .map(list::get).forEach(System.out::println); // 5 7 3 3 1

Créez une reverseIterable personnalisée.

6
nanda

Si les listes sont assez petites pour que les performances ne soient pas un réel problème, on peut utiliser la méthode reverse- de la classe Lists de Google Guava. Rend joli for-each- code, et la liste originale reste la même. De plus, la liste inversée est accompagnée de la liste d'origine. Toute modification apportée à la liste d'origine sera donc reflétée dans la liste inversée.

import com.google.common.collect.Lists;

[...]

final List<String> myList = Lists.newArrayList("one", "two", "three");
final List<String> myReverseList = Lists.reverse(myList);

System.out.println(myList);
System.out.println(myReverseList);

myList.add("four");

System.out.println(myList);
System.out.println(myReverseList);

Donne le résultat suivant:

[one, two, three]
[three, two, one]
[one, two, three, four]
[four, three, two, one]

Ce qui signifie que l'itération inverse de myList peut être écrite comme suit:

for (final String someString : Lists.reverse(myList)) {
    //do something
}
5
Tobb

Voici une implémentation (non testée) d'un ReverseIterable. Lorsque iterator() est appelé, il crée et retourne une implémentation privée ReverseIterator, qui mappe simplement les appels à hasNext() à hasPrevious() et les appels à next() sont mappés à previous(). Cela signifie que vous pouvez itérer sur un ArrayList en sens inverse:

ArrayList<String> l = ...
for (String s : new ReverseIterable(l)) {
  System.err.println(s);
}

Définition de classe

public class ReverseIterable<T> implements Iterable<T> {
  private static class ReverseIterator<T> implements Iterator {
    private final ListIterator<T> it;

    public boolean hasNext() {
      return it.hasPrevious();
    }

    public T next() {
      return it.previous();
    }

    public void remove() {
      it.remove();
    }
  }

  private final ArrayList<T> l;

  public ReverseIterable(ArrayList<T> l) {
    this.l = l;
  }

  public Iterator<T> iterator() {
    return new ReverseIterator(l.listIterator(l.size()));
  }
}
4
Adamski

Exemple très simple:

List<String> list = new ArrayList<String>();

list.add("ravi");

list.add("kant");

list.add("soni");

// Iterate to disply : result will be as ---     ravi kant soni

for (String name : list) {
  ...
}

//Now call this method

Collections.reverse(list);

// iterate and print index wise : result will be as ---     soni kant ravi

for (String name : list) {
  ...
}
3
Ravi Kant Soni
3
Andrejs

Aussi trouvé google collections inverse méthode.

2
Allain Lalonde

Pour avoir un code qui ressemble à ceci:

List<Item> items;
...
for (Item item : In.reverse(items))
{
    ...
}

Placez ce code dans un fichier appelé "In.Java":

import Java.util.*;

public enum In {;
    public static final <T> Iterable<T> reverse(final List<T> list) {
        return new ListReverseIterable<T>(list);
    }

    class ListReverseIterable<T> implements Iterable<T> {
        private final List<T> mList;

        public ListReverseIterable(final List<T> list) {
            mList = list;
        }

        public Iterator<T> iterator() {
            return new Iterator<T>() {
                final ListIterator<T> it = mList.listIterator(mList.size());

                public boolean hasNext() {
                    return it.hasPrevious();
                }
                public T next() {
                    return it.previous();
                }
                public void remove() {
                    it.remove();
                }
            };
        }
    }
}
2
intrepidis

Comme cela a été suggéré au moins deux fois, vous pouvez utiliser descendingIterator avec un Deque, en particulier avec un LinkedList. Si vous souhaitez utiliser la boucle for-each (c'est-à-dire avoir un Iterable), vous pouvez construire et utiliser un wraper comme celui-ci:

import Java.util.*;

public class Main {

    public static class ReverseIterating<T> implements Iterable<T> {
        private final LinkedList<T> list;

        public ReverseIterating(LinkedList<T> list) {
            this.list = list;
        }

        @Override
        public Iterator<T> iterator() {
            return list.descendingIterator();
        }
    }

    public static void main(String... args) {
        LinkedList<String> list = new LinkedList<String>();
        list.add("A");
        list.add("B");
        list.add("C");
        list.add("D");
        list.add("E");

        for (String s : new ReverseIterating<String>(list)) {
            System.out.println(s);
        }
    }
}
0
masterxilo