Iterazione di un elenco in ordine inverso in java

Sto migrando un pezzo di codice per fare uso di generici. Un argomento per farlo è che il ciclo for è molto più pulito di tenere traccia degli indici, o usare un iteratore esplicito.

In circa la metà dei casi, l’elenco (un object ArrayList) viene iterato in ordine inverso usando un indice oggi.

Qualcuno può suggerire un modo più pulito di farlo (poiché non mi piace il indexed for loop quando si lavora con le raccolte), sebbene funzioni?

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

Nota: non posso aggiungere nuove dipendenze al di fuori del JDK.

Prova questo:

 // 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()); } 

Guava offre le Lists#reverse(List) e ImmutableList#reverse() . Come nella maggior parte dei casi per Guava, l’ex delegato a quest’ultimo se l’argomento è una ImmutableList , quindi puoi usare il primo in tutti i casi. Questi non creano nuove copie della lista ma solo “viste invertite” di essa.

Esempio

 List reversed = ImmutableList.copyOf(myList).reverse(); 

Non penso sia ansible utilizzare la syntax del ciclo for. L’unica cosa che posso suggerire è di fare qualcosa come:

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

… ma non direi che questo è “più pulito” dato che sarà meno efficiente.

Opzione 1: hai pensato di invertire la lista con Collections # reverse () e poi usare foreach?

Ovviamente, potresti anche voler refactoring il tuo codice in modo che l’elenco sia ordinato correttamente in modo da non doverlo invertire, che utilizza spazio / tempo extra.


MODIFICARE:

Opzione 2: In alternativa, potresti usare un Deque invece di un ArrayList? Ti permetterà di scorrere avanti e indietro


MODIFICARE:

Opzione 3: come altri hanno suggerito, potresti scrivere un Iterator che passerà attraverso la lista al contrario, ecco un esempio:

 import java.util.Iterator; import java.util.List; public class ReverseIterator implements Iterator, Iterable { private final List list; private int position; public ReverseIterator(List list) { this.list = list; this.position = list.size() - 1; } @Override public Iterator 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 list = new ArrayList(); list.add("A"); list.add("B"); list.add("C"); list.add("D"); list.add("E"); for (String s : new ReverseIterator(list)) { System.out.println(s); } 

È ansible utilizzare la class concreta LinkedList anziché l’ List dell’interfaccia generale. Quindi hai un Descending Iterator per iterare con la direzione inversa.

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

Non so perché non esiste un descendingIterator ArrayList con ArrayList

Se gli elenchi sono abbastanza piccoli in modo che le prestazioni non siano un problema reale, è ansible utilizzare il metodo di inversione degli Lists -class in Google Guava . Rende piuttosto for-each codice, e l’elenco originale rimane lo stesso. Inoltre, l’elenco invertito è supportato dall’elenco originale, quindi qualsiasi modifica all’elenco originale si rifletterà in quella invertita.

 import com.google.common.collect.Lists; [...] final List myList = Lists.newArrayList("one", "two", "three"); final List myReverseList = Lists.reverse(myList); System.out.println(myList); System.out.println(myReverseList); myList.add("four"); System.out.println(myList); System.out.println(myReverseList); 

Produce il seguente risultato:

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

Il che significa che l’iterazione inversa di myList può essere scritta come:

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

Crea un reverseIterable personalizzato.

Questa è una vecchia domanda, ma manca una risposta amichevole a java8. Ecco alcuni modi per invertire la iterazione dell’elenco, con l’aiuto dell’API Streaming:

 List list = new ArrayList(Arrays.asList(1, 3, 3, 7, 5)); list.stream().forEach(System.out::println); // 1 3 3 7 5 int size = list.size(); ListIterator it = list.listIterator(size); Stream.generate(it::previous).limit(size) .forEach(System.out::println); // 5 7 3 3 1 ListIterator 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 (ie 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 (ie 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 

Ecco un’implementazione (non testata) di ReverseIterable . Quando viene chiamato iterator() , crea e restituisce ReverseIterator privata di ReverseIterator , che mappa semplicemente le chiamate a hasNext() a hasPrevious() e le chiamate a next() vengono mappate a previous() . Significa che è ansible eseguire iterazioni su una ArrayList al contrario come segue:

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

Definizione di class

 public class ReverseIterable implements Iterable { private static class ReverseIterator implements Iterator { private final ListIterator it; public boolean hasNext() { return it.hasPrevious(); } public T next() { return it.previous(); } public void remove() { it.remove(); } } private final ArrayList l; public ReverseIterable(ArrayList l) { this.l = l; } public Iterator iterator() { return new ReverseIterator(l.listIterator(l.size())); } } 

Esempio molto semplice:

 List list = new ArrayList(); 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) { ... } 

Trovato anche metodo inverso di raccolte di Google.

Per avere un codice che assomiglia a questo:

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

Inserisci questo codice in un file chiamato “In.java”:

 import java.util.*; public enum In {; public static final  Iterable reverse(final List list) { return new ListReverseIterable(list); } class ListReverseIterable implements Iterable { private final List mList; public ListReverseIterable(final List list) { mList = list; } public Iterator iterator() { return new Iterator() { final ListIterator it = mList.listIterator(mList.size()); public boolean hasNext() { return it.hasPrevious(); } public T next() { return it.previous(); } public void remove() { it.remove(); } }; } } } 

Come è stato suggerito almeno due volte, puoi usare descendingIterator con un Deque , in particolare con LinkedList . Se vuoi usare il ciclo for-each (cioè, avere un Iterable ), puoi build e usare un wrapper come questo:

 import java.util.*; public class Main { public static class ReverseIterating implements Iterable { private final LinkedList list; public ReverseIterating(LinkedList list) { this.list = list; } @Override public Iterator iterator() { return list.descendingIterator(); } } public static void main(String... args) { LinkedList list = new LinkedList(); list.add("A"); list.add("B"); list.add("C"); list.add("D"); list.add("E"); for (String s : new ReverseIterating(list)) { System.out.println(s); } } } 

Motivo: “Non so perché non ci sia Descending Iterator con ArrayList …”

Poiché l’elenco di array non mantiene l’elenco nello stesso ordine in cui i dati sono stati aggiunti all’elenco. Quindi, non usare mai Arraylist.

L’elenco collegato manterrà i dati nello stesso ordine di AGGIUNGI alla lista.

Quindi, sopra nel mio esempio, ho usato ArrayList () per fare in modo che l’utente distorca la sua mente e le faccia esercitare qualcosa dalla propria parte.

Invece di questo

 List list = new ArrayList(); 

USO:

 List list = new LinkedList(); 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) { ... }