Bisakah seseorang melakukan untuk setiap loop di java dalam urutan terbalik?

148

Saya perlu menjalankan Daftar dalam urutan terbalik menggunakan Java.

Jadi di mana ini dilakukan:

for(String string: stringList){
//...do something
}

Apakah ada beberapa cara untuk mengulangi stringList dalam urutan terbalik menggunakan untuk setiap sintaks?

Untuk kejelasan: Saya tahu bagaimana cara mengulang daftar dalam urutan terbalik tetapi ingin tahu (demi rasa ingin tahu) bagaimana melakukannya dalam setiap gaya.

Ron Tuffin
sumber
5
Inti dari loop "untuk-setiap" adalah Anda hanya perlu melakukan operasi pada setiap elemen, dan urutan tidak penting. Untuk-masing-masing dapat memproses elemen dalam urutan yang benar-benar acak, dan masih akan melakukan apa yang dirancang untuk itu. Jika Anda perlu memproses elemen-elemen dengan cara tertentu, saya sarankan melakukannya secara manual.
muusbolla
Perpustakaan koleksi Java. Tidak benar-benar ada hubungannya dengan bahasa. Salahkan Josh Bloch.
Tom Hawtin - tackline
7
@muusbolla: Tapi karena Daftar adalah koleksi yang dipesan , pasti pesanannya akan dihormati, terlepas? Oleh karena itu untuk-masing-masing tidak akan memproses elemen Daftar dalam urutan acak.
Lee Kowalkowski
5
@muusbolla itu tidak benar. Mungkin dalam kasus Setkoleksi turunan. foreachmenjamin iterasi dalam urutan iterator yang dikembalikan dari iterator()metode pengumpulan. docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
robert

Jawaban:

151

Metode Collections.reverse sebenarnya mengembalikan daftar baru dengan elemen-elemen dari daftar asli disalin ke dalam urutan terbalik, jadi ini memiliki kinerja O (n) berkaitan dengan ukuran daftar asli.

Sebagai solusi yang lebih efisien, Anda dapat menulis dekorator yang menyajikan pandangan terbalik dari Daftar sebagai Iterable. Iterator yang dikembalikan oleh dekorator Anda akan menggunakan ListIterator dari daftar yang dihiasi untuk berjalan di atas elemen dalam urutan terbalik.

Sebagai contoh:

public class Reversed<T> implements Iterable<T> {
    private final List<T> original;

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

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

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

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

Dan Anda akan menggunakannya seperti:

import static Reversed.reversed;

...

List<String> someStrings = getSomeStrings();
for (String s : reversed(someStrings)) {
    doSomethingWith(s);
}
Nat
sumber
22
Itu pada dasarnya apa Iterables.reverse Google tidak, ya :)
Jon Skeet
10
Saya tahu ada 'aturan' bahwa kita harus menerima jawaban Jon :) tapi .. Saya ingin menerima yang ini (meskipun pada dasarnya mereka sama) karena tidak mengharuskan saya untuk memasukkan perpustakaan pihak ke-3 lainnya (walaupun beberapa orang dapat berargumen bahwa alasan itu mematahkan salah satu keunggulan utama OO - reusability).
Ron Tuffin
Kesalahan kecil: Dalam public void remove (), seharusnya tidak ada pernyataan kembali, itu harus hanya: i.remove ();
Jesper
10
Collections.reverse () TIDAK mengembalikan salinan yang dibalik tetapi bertindak pada Daftar yang diteruskan sebagai parameter. Suka solusi Anda dengan iterator. Sangat elegan.
er4z0r
96

Untuk daftar, Anda dapat menggunakan Perpustakaan Google Guava :

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

Catatan yang tidak membalikkan seluruh koleksi, atau melakukan hal seperti itu - itu hanya memungkinkan iterasi dan akses acak, dalam urutan terbalik. Ini lebih efisien daripada membalikkan koleksi terlebih dahulu.Lists.reverse

Untuk membalikkan iterable yang sewenang-wenang, Anda harus membaca semuanya dan kemudian "memutar ulang" itu mundur.

(Jika Anda belum menggunakannya, saya benar-benar akan merekomendasikan Anda melihat Guava . Ini hal yang hebat.)

Jon Skeet
sumber
1
Basis kode kami banyak menggunakan versi umum dari Commons Collections yang dirilis oleh larvalab ( larvalabs.com/collections ). Melihat melalui repo SVN untuk Apache Commons, jelas bahwa sebagian besar pekerjaan dalam merilis versi java 5 dari Commons Collections sudah selesai, mereka belum merilisnya.
skaffman
Saya suka itu. Jika tidak begitu berguna, saya akan menyebutnya plug.
geowa4
Saya heran mengapa Jakarta tidak pernah repot untuk memperbarui Apache Commons.
Uri
3
Mereka telah memperbaruinya, itulah yang saya katakan. Mereka belum merilisnya.
skaffman
23
Iterables.reverse telah ditinggalkan, gunakan Lists.reverse atau ImmutableList.reverse sebagai gantinya.
Garrett Hall
39

Daftar (tidak seperti Perangkat) adalah koleksi yang dipesan dan iterasi dari itu memang mempertahankan pesanan dengan kontrak. Saya akan mengharapkan Stack untuk beralih dalam urutan terbalik tapi sayangnya tidak. Jadi solusi paling sederhana yang dapat saya pikirkan adalah ini:

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

Saya menyadari bahwa ini bukan solusi loop "untuk setiap". Saya lebih suka menggunakan for for daripada memperkenalkan perpustakaan baru seperti Google Collections.

Collections.reverse () juga melakukan pekerjaan tetapi memperbarui daftar yang bertentangan dengan mengembalikan salinan dalam urutan terbalik.

Gopi Reddy
sumber
3
Pendekatan ini mungkin baik untuk daftar berbasis array (seperti ArrayList) tetapi itu akan kurang optimal untuk Linked Linked karena setiap get harus melintasi daftar dari awal sampai akhir (atau mungkin ujung ke awal) untuk setiap get. Lebih baik menggunakan iterator yang lebih pintar seperti pada solusi Nat (optimal untuk semua implementasi Daftar).
Chris
1
Lebih jauh, itu menyimpang dari permintaan dalam OP yang secara eksplisit meminta for eachsintaksis
Paul W
8

Ini akan berantakan dengan daftar asli dan juga perlu dipanggil di luar loop. Anda juga tidak ingin melakukan pembalikan setiap kali Anda mengulang - apakah itu benar jika salah satu Iterables.reverse ideasditerapkan?

Collections.reverse(stringList);

for(String string: stringList){
//...do something
}
Phillip Gibb
sumber
5

AFAIK tidak ada jenis "reverse_iterator" standar di pustaka standar yang mendukung sintaks untuk masing-masing yang sudah menjadi gula sintaksis yang mereka bawa terlambat ke dalam bahasa.

Anda dapat melakukan sesuatu seperti untuk (elemen Item: myList.clone (). Reverse ()) dan membayar harga yang terkait.

Ini juga tampaknya cukup konsisten dengan fenomena yang tampak tidak memberikan Anda cara mudah untuk melakukan operasi mahal - karena daftar, menurut definisi, dapat memiliki kompleksitas akses acak O (N) (Anda dapat mengimplementasikan antarmuka dengan satu-link), membalikkan iterasi bisa menjadi O (N ^ 2). Tentu saja, jika Anda memiliki ArrayList, Anda tidak membayar harga itu.

Uri
sumber
Anda dapat menjalankan ListIterator ke belakang, yang dapat dibungkus dengan Iterator.
Tom Hawtin - tackline
@ Tom: Poin bagus. Namun, dengan iterator Anda masih melakukan gaya lama yang mengganggu untuk loop, dan Anda mungkin masih membayar biaya untuk sampai ke elemen terakhir untuk memulai dengan ... Saya menambahkan kualifikasi dalam jawaban saya, meskipun, terima kasih.
Uri
Deque memiliki iterator terbalik.
Michael Munsey
2

Ini mungkin opsi. Berharap ada cara yang lebih baik untuk memulai dari elemen terakhir daripada loop sementara sampai akhir.

public static void main(String[] args) {        
    List<String> a = new ArrayList<String>();
    a.add("1");a.add("2");a.add("3");a.add("4");a.add("5");

    ListIterator<String> aIter=a.listIterator();        
    while(aIter.hasNext()) aIter.next();

    for (;aIter.hasPrevious();)
    {
        String aVal = aIter.previous();
        System.out.println(aVal);           
    }
}
Krishna
sumber
2

Pada komentar : Anda harus dapat menggunakan Apache CommonsReverseListIterator

Iterable<String> reverse 
    = new IteratorIterable(new ReverseListIterator(stringList));

for(String string: reverse ){
    //...do something
}

Seperti yang dikatakan @rogerdpack , Anda harus membungkusnya ReverseListIteratorsebagai Iterable.

serv-inc
sumber
1

Bukan tanpa menulis beberapa kode khusus yang akan memberi Anda enumerator yang akan membalikkan elemen untuk Anda.

Anda harus dapat melakukannya di Jawa dengan membuat implementasi kustom Iterable yang akan mengembalikan elemen dalam urutan terbalik.

Kemudian, Anda akan instantiate wrapper (atau memanggil metode, apa-punya-Anda) yang akan mengembalikan implementasi Iterable yang membalikkan elemen di dalam untuk setiap loop.

casperOne
sumber
1

Anda harus membalikkan koleksi Anda jika Anda ingin menggunakan untuk setiap sintaks di luar kotak dan pergi dalam urutan terbalik.

Owen
sumber
1

Semua jawaban di atas hanya memenuhi persyaratan, baik dengan membungkus metode lain atau memanggil beberapa kode asing di luar;

Berikut adalah solusi yang disalin dari Thinking in Java edisi ke 4 , bab 11.13.1 AdapterMethodIdiom ;

Ini kodenya:

// The "Adapter Method" idiom allows you to use foreach
// with additional kinds of Iterables.
package holding;
import java.util.*;

@SuppressWarnings("serial")
class ReversibleArrayList<T> extends ArrayList<T> {
  public ReversibleArrayList(Collection<T> c) { super(c); }
  public Iterable<T> reversed() {
    return new Iterable<T>() {
      public Iterator<T> iterator() {
        return new Iterator<T>() {
          int current = size() - 1; //why this.size() or super.size() wrong?
          public boolean hasNext() { return current > -1; }
          public T next() { return get(current--); }
          public void remove() { // Not implemented
            throw new UnsupportedOperationException();
          }
        };
      }
    };
  }
}   

public class AdapterMethodIdiom {
  public static void main(String[] args) {
    ReversibleArrayList<String> ral =
      new ReversibleArrayList<String>(
        Arrays.asList("To be or not to be".split(" ")));
    // Grabs the ordinary iterator via iterator():
    for(String s : ral)
      System.out.print(s + " ");
    System.out.println();
    // Hand it the Iterable of your choice
    for(String s : ral.reversed())
      System.out.print(s + " ");
  }
} /* Output:
To be or not to be
be to not or be To
*///:~
qiwen li
sumber
mengapa itu int current = size() - 1benar? mengapa tidak int current = this.size() - 1atauint current = super.size() - 1
Qiwen li
1

A Work Around:

Collections.reverse(stringList).forEach(str -> ...);

Atau dengan jambu biji :

Lists.reverse(stringList).forEach(str -> ...);
EssaidiM
sumber
0

Jelas merupakan jawaban yang terlambat untuk pertanyaan ini. Salah satu kemungkinan adalah menggunakan ListIterator dalam for for loop. Ini tidak sebersih sintaksis titik dua, tetapi bekerja.

List<String> exampleList = new ArrayList<>();
exampleList.add("One");
exampleList.add("Two");
exampleList.add("Three");

//Forward iteration
for (String currentString : exampleList) {
    System.out.println(currentString); 
}

//Reverse iteration
for (ListIterator<String> itr = exampleList.listIterator(exampleList.size()); itr.hasPrevious(); /*no-op*/ ) {
    String currentString = itr.previous();
    System.out.println(currentString); 
}

Kredit untuk sintaksis ListIterator masuk ke "Cara untuk mengulangi daftar di Jawa"

ScottMichaud
sumber