Jak uzyskać odwrócony widok listy na liście w Javie?

214

Chcę mieć odwrócony widok listy na liście (w podobny sposób, jak List#sublistzapewnia widok listy sublist na liście). Czy jest jakaś funkcja zapewniająca tę funkcjonalność?

Nie chcę tworzyć żadnej kopii listy ani modyfikować listy.

Wystarczyłoby, gdybym w tym przypadku znalazł przynajmniej jedną iterator do tyłu.


Wiem też, jak to zrobić. Pytam tylko, czy Java już zapewnia coś takiego.

Wdrożenie demonstracyjne:

static <T> Iterable<T> iterableReverseList(final List<T> l) {
    return new Iterable<T>() {
        public Iterator<T> iterator() {
            return new Iterator<T>() {
                ListIterator<T> listIter = l.listIterator(l.size());                    
                public boolean hasNext() { return listIter.hasPrevious(); }
                public T next() { return listIter.previous(); }
                public void remove() { listIter.remove(); }                 
            };
        }
    };
}

Właśnie dowiedziałem się, że niektóre Listwdrożenia mają descendingIterator()to, czego potrzebuję. Chociaż nie ma takiej ogólnej implementacji List. Co jest dość dziwne, ponieważ implementacja, którą widziałem, LinkedListjest wystarczająco ogólna, aby z nią pracować List.

Albert
źródło
1
Czy na początku możesz zbudować listę w odwrotnej kolejności?
Tony Ennis,
Tak, robi - java.uitl.List.listIterator (int) download.oracle.com/javase/6/docs/api/java/util/…
TofuBeer 18.10.10
Jeśli odwiedzisz ten link, aby znaleźć odpowiedź na temat odwrócenia (modyfikacji) listy, oto odpowiedź:Collections.reverse(list)
ZhaoGang,

Odpowiedzi:

210

Guava zapewnia to: Lists.reverse (List)

List<String> letters = ImmutableList.of("a", "b", "c");
List<String> reverseView = Lists.reverse(letters); 
System.out.println(reverseView); // [c, b, a]

W przeciwieństwie do Collections.reversetego jest to wyłącznie widok ... nie zmienia kolejności elementów na oryginalnej liście. Ponadto w przypadku modyfikowalnej oryginalnej listy zmiany zarówno pierwotnej listy, jak i widoku znajdują odzwierciedlenie w drugiej.

ColinD
źródło
11
Problem polega na tym, że Guava to bardzo duża biblioteka. Zobacz dyskusję: github.com/google/guava/issues/1954 i code.google.com/p/guava-libraries/issues/detail?id=605
Filipe Brito
2
@Filipe de Lima Brito: ProGuard jest nadal najlepszym rozwiązaniem dla rozmiaru biblioteki, choć możliwe są ulepszenia, które możemy wprowadzić. W każdym razie nie sądzę, aby rozmiar biblioteki w jakikolwiek sposób wpłynął na tę odpowiedź.
ColinD
2
Tak, rozmiar biblioteki nie ma związku z tą odpowiedzią, ale należy poinformować programistów (dlatego skomentowałem)! Bardzo dziękuję za tę wspaniałą bibliotekę i za sugestię @ColinD!
Filipe Brito
@ColinD, tak, to był mój błąd. Kolega dodał kolekcje google, które również mają tę samą przestrzeń nazw i klasę ( List), ale bez metody odwrotnej. usunięcie go sprawiło, że guawa znów była dostępna.
AaA
Programiści nie zawsze mają kontrolę nad bibliotekami, których mogą używać, a dodanie całej nowej biblioteki dla czegoś tak prostego wydaje się przesadą - zwłaszcza biorąc pod uwagę, że problem można rozwiązaćListIterator.previous()
Jonathan Benn
218

Użyj metody .clone () na swojej liście. Zwróci płytką kopię, co oznacza, że ​​będzie zawierała wskaźniki do tych samych obiektów, więc nie będziesz musiał kopiować listy. Następnie skorzystaj z kolekcji.

Ergo

Collections.reverse(list.clone());

Jeśli używasz Listi nie masz dostępu clone(), możesz użyć subList():

List<?> shallowCopy = list.subList(0, list.size());
Collections.reverse(shallowCopy);
jcalvert
źródło
21
clone()normalnie tworzyłby kopię listy. Zresztą List#clone()też nie istnieje.
Albert
6
Masz techniczną rację, sam interfejs List nie zapewnia metody clone (). Ale wszystkie ArrayList, LinkedList i Vector.
jcalvert
2
Właśnie sprawdziłem wdrożenie clone(). Rzeczywiście robi pełną kopię listy (nie klonuje tylko każdego obiektu na liście, ale o tym nigdy nie mówiłem).
Albert
21
Zauważ, że Collections.reverse zwraca void, więc stracisz referencję do klonowania. Najpierw musisz przypisać klon do zmiennej, a następnie posortować.
16.04.15
10
subListnie kopiuje, po prostu zapewnia widok na liście bazowej, dlatego odwrócenie tego widoku spowoduje odwrócenie listy bazowej.
Roland,
80

Jeśli dobrze zrozumiałem, to jest to jeden wiersz kodu. To zadziałało dla mnie.

 Collections.reverse(yourList);
Shakeeb Ayaz
źródło
12
To nie jest widok listy. To modyfikuje listę.
Albert,
35

Nie jest do końca elegancki, ale jeśli użyjesz List.listIterator (int index), możesz uzyskać dwukierunkowy ListIterator na końcu listy:

//Assume List<String> foo;
ListIterator li = foo.listIterator(foo.size());

while (li.hasPrevious()) {
   String curr = li.previous()
}
kkress
źródło
1
To najlepsza odpowiedź, ponieważ (1) nie wymaga biblioteki i (2) nie modyfikuje oryginalnej listy, zgodnie z żądaniem PO
Jonathan Benn
12

Collections.reverse (nums) ... Faktycznie odwraca kolejność elementów. Poniższy kod należy docenić -

List<Integer> nums = new ArrayList<Integer>();
nums.add(61);
nums.add(42);
nums.add(83);
nums.add(94);
nums.add(15);
//Tosort the collections uncomment the below line
//Collections.sort(nums); 

Collections.reverse(nums);

System.out.println(nums);

Wyjście: 15,94,83,42,61

Krishna Kumar Chourasiya
źródło
8
Powtarzasz tylko odpowiedź, którą ktoś napisał 6 lat temu
Jonathan Benn
1
... a nie widok. to mutuje listę.
Jason S
6

java.util.Dequema descendingIterator()- jeśli Listjest Deque, można użyć.

Bozho
źródło
Jeśli nie chcesz używać wbudowanej metody descndingIterator (), wydaje się, że użycie ConcurrentLinkedDeque byłoby najlepszym rozwiązaniem do odwrócenia bardzo dużej listy? Zasadniczo wystarczy skopiować z jednej talii do nowej za pomocą ankiety, a następnie zaoferować? To tak, jakby po prostu mieć talię kart i zdejmować każdą z góry na nowy stos.
djangofan
4

Wiem, że to stary post, ale dzisiaj szukałem czegoś takiego. Na koniec sam napisałem kod:

private List reverseList(List myList) {
    List invertedList = new ArrayList();
    for (int i = myList.size() - 1; i >= 0; i--) {
        invertedList.add(myList.get(i));
    }
    return invertedList;
}

Niezalecane w przypadku długich list, nie jest to wcale zoptymalizowane. Jest to dość łatwe rozwiązanie dla kontrolowanych scenariuszy (obsługiwane przez mnie Listy zawierają nie więcej niż 100 elementów).

Mam nadzieję, że to komuś pomoże.

CocheLee
źródło
2
Twój kod ma jeden problem - możesz umieścić na nim dowolną Listę, ale zawsze zwróci Ci ArrayList (jako List). A co jeśli potrzebuję LinkedList? Lepiej zmodyfikować myList i zwrócić void.
Dmitrij Zajcew
2
Zauważ, że tak naprawdę nie o to prosiłem. Prosiłam o jakiś proxy / widok, a nie o kopię.
Albert
4

Używam tego:

public class ReversedView<E> extends AbstractList<E>{

    public static <E> List<E> of(List<E> list) {
        return new ReversedView<>(list);
    }

    private final List<E> backingList;

    private ReversedView(List<E> backingList){
        this.backingList = backingList;
    }

    @Override
    public E get(int i) {
        return backingList.get(backingList.size()-i-1);
    }

    @Override
    public int size() {
        return backingList.size();
    }

}

lubię to:

ReversedView.of(backingList) // is a fully-fledged generic (but read-only) list
Wesoły
źródło
1

Możesz także to zrobić:

static ArrayList<String> reverseReturn(ArrayList<String> alist)
{
   if(alist==null || alist.isEmpty())
   { 
       return null;
   }

   ArrayList<String> rlist = new ArrayList<>(alist);

   Collections.reverse(rlist);
   return rlist;
}
jhdrosos
źródło
5
To nie jest widok listy. Widok jest przeciwieństwem kopii.
Albert,
Odwrotna lista pustej listy jest pusta?
ShellFish,
1

Możesz także odwrócić pozycję, gdy zażądasz obiektu:

Object obj = list.get(list.size() - 1 - position);
fede1608
źródło
1

W przypadku małej listy możemy utworzyć, LinkedLista następnie użyć iteratora malejącego jako:

List<String> stringList = new ArrayList<>(Arrays.asList("One", "Two", "Three"));
stringList.stream().collect(Collectors.toCollection(LinkedList::new))
         .descendingIterator().
         forEachRemaining(System.out::println); // Three, Two, One
System.out.println(stringList); // One, Two, Three
akhil_mittal
źródło
-3

Użyj reverse(...)metod java.util.Collectionsklasy. Przekaż listę jako parametr, a lista zostanie odwrócona.

Collections.reverse(list);
Shubho Ghosh
źródło
1
Kopia istniejącej odpowiedzi
Karl Richter,