Een lijst doorlopen in omgekeerde volgorde in Java

Ik migreer een stukje code om gebruik te maken van generieke geneesmiddelen. Een argument hiervoor is dat de for-lus veel schoner is dan het bijhouden van indexen of het gebruik van een expliciete iterator.

In ongeveer de helft van de gevallen wordt de lijst (een ArrayList) vandaag in omgekeerde volgorde herhaald met behulp van een index.

Kan iemand een schonere manier voorstellen om dit te doen (aangezien ik een hekel heb aan de indexed for loopwanneer ik met verzamelingen werk), hoewel het wel werkt?

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

Opmerking:ik kan geen nieuwe afhankelijkheden toevoegen buiten de JDK.


Antwoord 1, autoriteit 100%

Probeer dit:

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

Antwoord 2, autoriteit 8%

Guavabiedt Lists#reverse(List)en ImmutableList#reverse(). Zoals in de meeste gevallen voor Guava, delegeert de eerste naar de laatste als het argument een ImmutableListis, dus je kunt de eerste in alle gevallen gebruiken. Deze maken geen nieuwe kopieën van de lijst, maar alleen “omgekeerde weergaven” ervan.

Voorbeeld

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

Antwoord 3, autoriteit 5%

Ik denk niet dat het mogelijk is om de for loop-syntaxis te gebruiken. Het enige dat ik kan voorstellen is om iets te doen als:

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

… maar ik zou niet zeggen dat dit “schoner” is, aangezien het minder efficiënt zal zijn.


Antwoord 4, autoriteit 3%

Optie 1: Heb je erover nagedacht om de lijst terug te draaien met Collecties#reverse()en vervolgens foreach gebruiken?

Natuurlijk wilt u uw code misschien ook zo aanpassen dat de lijst correct is geordend, zodat u deze niet hoeft om te draaien, wat extra ruimte/tijd in beslag neemt.


BEWERKEN:

Optie 2: kun je ook een deque gebruiken in plaats van een ArrayList? Hiermee kunt u voorwaarts en achterwaarts herhalen


BEWERKEN:

Optie 3: Zoals anderen hebben gesuggereerd, zou je een Iterator kunnen schrijven die de lijst in omgekeerde volgorde doorloopt, hier is een voorbeeld:

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

Antwoord 5, autoriteit 3%

Je zou de concrete klasse LinkedListkunnen gebruiken in plaats van de algemene interface List. Dan heb je een descendingIteratorvoor iteratie met de omgekeerde richting.

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

Weet niet waarom er geen descendingIteratoris met ArrayList


Antwoord 6, autoriteit 2%

Dit is een oude vraag, maar er ontbreekt een java8-vriendelijk antwoord. Hier zijn enkele manieren om de lijst in omgekeerde volgorde te herhalen, met behulp van de Streaming API:

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

Antwoord 7

Hier is een (niet-geteste) implementatie van een reverseIterable. Wanneer iterator()wordt aangeroepen, wordt een privé ReverseIterator-implementatie gemaakt en geretourneerd, die eenvoudig oproepen naar hasNext()toewijst aan hasPrevious()en oproepen naar next()worden toegewezen aan previous(). Dit betekent dat u een ArrayListals volgt in omgekeerde volgorde kunt herhalen:

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

Klassedefinitie

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

Antwoord 8

Als de lijsten vrij klein zijn, zodat prestatie geen echt probleem is, kan men de reverse-methode van de Lists-klasse gebruiken in Google Guava. Levert mooie for-each-code op, en de originele lijst blijft hetzelfde. De omgekeerde lijst wordt ook ondersteund door de originele lijst, dus elke wijziging aan de originele lijst wordt weerspiegeld in de omgekeerde lijst.

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);

Geeft het volgende resultaat:

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

Wat betekent dat omgekeerde iteratie van myList kan worden geschreven als:

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

Antwoord 9

Je zou ReverseListIteratorvan Apache Commons-Collections kunnen gebruiken:

https:/ /commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/ReverseListIterator.html


Antwoord 10

Heel eenvoudig voorbeeld:

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) {
  ...
}

Antwoord 11

Maak een aangepaste reverseIterable.


Antwoord 12

Ook Google-collecties gevonden omgekeerdemethode.


Antwoord 13

Om code te hebben die er als volgt uitziet:

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

Zet deze code in een bestand met de naam “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();
                }
            };
        }
    }
}

Antwoord 14

Zoals minstens twee keer is gesuggereerd, kunt u descendingIteratorgebruiken met een Deque, in het bijzonder met een LinkedList. Als u de for-each-lus wilt gebruiken (d.w.z. een Iterablehebben), kunt u een wraper als volgt construeren en gebruiken:

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

Antwoord 15

Valid for Java 9+
List<String> strList = List.of("a", "b", "c", "d", "e");
IntStream.iterate(strList.size() - 1, i -> i >= 0, i -> --i)
         .mapToObj(strList::get)
         .forEach(System.out::println);

Antwoord 16

Reden: “Weet niet waarom er geen aflopendeIterator is met ArrayList…”

Omdat de arraylijst de lijst niet in dezelfde volgorde houdt als de gegevens aan de lijst zijn toegevoegd. Gebruik daarom nooit Arraylist .

Gelinkte lijst houdt de gegevens in dezelfde volgorde van TOEVOEGEN aan lijst.

Dus, hierboven in mijn voorbeeld, heb ik ArrayList() gebruikt om de gebruiker aan het denken te zetten en hem iets van zijn kant te laten trainen.

In plaats van dit

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

GEBRUIK:

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

Other episodes