Ubah Iterator ke ArrayList

241

Mengingat Iterator<Element>, bagaimana kita dapat mengkonversi Iteratorke ArrayList<Element>(atau List<Element>) di terbaik dan tercepat cara yang mungkin, sehingga kita dapat menggunakan ArrayList's operasi di atasnya seperti get(index), add(element), dll

Maksim
sumber

Jawaban:

360

Lebih baik gunakan perpustakaan seperti Jambu :

import com.google.common.collect.Lists;

Iterator<Element> myIterator = ... //some iterator
List<Element> myList = Lists.newArrayList(myIterator);

Contoh lain Jambu Biji:

ImmutableList.copyOf(myIterator);

atau Koleksi Apache Commons :

import org.apache.commons.collections.IteratorUtils;

Iterator<Element> myIterator = ...//some iterator

List<Element> myList = IteratorUtils.toList(myIterator);       
Renaud
sumber
8
Saya tidak mengerti. Dengan cara apa ArrayList dikembalikan oleh, katakanlah, Jambu lebih baik daripada ArrayList normal? Apakah mereka melakukannya dengan cara yang lebih efisien? Sekalipun lebih efisien, apakah perlu menambahkan ketergantungan ekstra (dan lebih banyak kompleksitas) pada proyek Anda?
CorayThan
10
@CorayThan lebih sedikit kode + metode yang diuji. Meskipun saya setuju dengan Anda, saya tidak akan menambahkan ketergantungan tambahan hanya untuk menggunakan metode itu. Tetapi sekali lagi, sebagian besar proyek (besar) saya menggunakan Guava atau Apache Commons ...
Renaud
4
@CorayThan Bagilah dan taklukkan teman saya. Mengapa menulis metode yang sudah disediakan oleh perpustakaan dan diuji? Kami menggunakan banyak Apache Commons dan Jambu, mereka hanya luar biasa dan membantu Anda menghemat waktu dan uang.
Stephan 3-15
5
Setuju dengan Renaud dan Stephan. Juga, jika Anda menggunakan alat bangun itu adalah hal termudah di dunia untuk menyertakan perpustakaan ini ... JUGA ... jika Anda benar-benar tidak ingin memasukkannya, Anda dapat pergi ke sumber kode Apache / Guava dan salin apa yang telah mereka lakukan di sana: JAUH lebih baik daripada membuang-buang waktu Anda menciptakan kembali ratusan roda yang direkayasa dan diuji dengan indah yang sudah ada di luar sana.
mike rodent
238

Di Java 8, Anda dapat menggunakan forEachRemainingmetode baru yang telah ditambahkan ke Iteratorantarmuka:

List<Element> list = new ArrayList<>();
iterator.forEachRemaining(list::add);
Stuart Marks
sumber
2
apa artinya ::? nama apa itu? tampaknya referensi langsung ke list.add (); dan sepertinya juga beberapa hal baru java8; dan terimakasih! :)
Aquarius Power
13
@AquariusPower ::Sintaksnya baru di Java 8, dan mengacu pada "referensi metode," yang merupakan bentuk singkatan dari lambda. Lihat di sini untuk info lebih lanjut: docs.oracle.com/javase/tutorial/java/javaOO/…
Stuart Marks
dari mana iteratordatangnya itu adalah simbol yang belum terselesaikan
javadba
1
@javadba Pertanyaan aslinya adalah tentang bagaimana mengkonversi iterator yang sudah Anda miliki menjadi ArrayList baru. Untuk keperluan contoh ini, iterator yang sudah Anda miliki diasumsikan dipanggil iterator.
Stuart Marks
1
Ini harus menjadi jawaban yang diterima, karena ini adalah yang terpendek dan tidak bergantung pada perpustakaan pihak ke-3
DanielCuadra
64

Anda dapat menyalin iterator ke daftar baru seperti ini:

Iterator<String> iter = list.iterator();
List<String> copy = new ArrayList<String>();
while (iter.hasNext())
    copy.add(iter.next());

Itu dengan asumsi bahwa daftar tersebut berisi string. Sebenarnya tidak ada cara yang lebih cepat untuk membuat ulang daftar dari iterator, Anda terjebak dengan melewatinya dengan tangan dan menyalin setiap elemen ke daftar baru dari jenis yang sesuai.

EDIT:

Berikut adalah metode umum untuk menyalin iterator ke daftar baru dengan cara yang aman:

public static <T> List<T> copyIterator(Iterator<T> iter) {
    List<T> copy = new ArrayList<T>();
    while (iter.hasNext())
        copy.add(iter.next());
    return copy;
}

Gunakan seperti ini:

List<String> list = Arrays.asList("1", "2", "3");
Iterator<String> iter = list.iterator();
List<String> copy = copyIterator(iter);
System.out.println(copy);
> [1, 2, 3]
Óscar López
sumber
26

Perhatikan ada perbedaan antara Iterabledan Iterator.

Jika Anda memiliki Iterable, maka dengan Java 8 Anda dapat menggunakan solusi ini:

Iterable<Element> iterable = createIterable();
List<Element> array = StreamSupport
    .stream(iterable.spliterator(), false)
    .collect(Collectors.toList());

Seperti yang saya tahu Collectors.toList()menciptakan ArrayListinstance.

Sebenarnya menurut saya, itu juga terlihat bagus dalam satu baris.
Misalnya jika Anda perlu kembali List<Element>dari beberapa metode:

return StreamSupport.stream(iter.spliterator(), false).collect(Collectors.toList());
Dub Nazar
sumber
4
Pertanyaannya adalah tentang Iterator <Element> sebagai titik awal, bukan Iterable <Element>.
Jaap
1
setuju dengan komentar di atas. Super membingungkan bahwa Anda memberi nama Anda Iterable iterator. Mereka benar-benar berbeda.
Stephen Harrison
Di Java 8 Anda dapat dengan mudah mengkonversi Iteratorkembali ke satu penggunaan Iterable dengan menggunakan () -> iterator. Ini bisa berguna dalam situasi seperti ini, tetapi izinkan saya menekankan hal yang penting lagi: Anda dapat menggunakan metode ini hanya jika satu penggunaan Iterable dapat diterima. Memanggil iterable.iterator()lebih dari satu kali akan menghasilkan hasil yang tidak terduga. Jawaban di atas kemudian menjadiIterator<Element> iterator = createIterator(); List<Element> array = StreamSupport.stream(((Iterable<Element>) () -> iterable).spliterator(), false).collect(toList());
neXus
9

Solusi ringkas dengan Java 8 sederhana menggunakan java.util.stream:

public static <T> ArrayList<T> toArrayList(final Iterator<T> iterator) {
    return StreamSupport
        .stream(
            Spliterators
                .spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
        .collect(
                Collectors.toCollection(ArrayList::new)
    );
}
xehpuk
sumber
Apakah ada cara yang lebih ringkas untuk menulis ini menggunakan aliran api? Tampaknya tidak lebih sederhana daripada cara loop sementara normal.
xi.lin
37
Saya tidak akan menyebut solusi itu "ringkas".
Sergio
1
@ Sergio Itu sebabnya saya menulis "cantik". Namun, itu tidak memerlukan variabel lokal dan hanya satu titik koma. Anda dapat mempersingkatnya dengan impor statis.
xehpuk
1
Saya akan mengatakan iterator.forEachRemaining( list::add ), juga baru di Java 8, jauh lebih ringkas. Mendorong variabel daftar ke Collectortidak meningkatkan readibility dalam hal ini, karena harus didukung oleh a Streamdan a Spliterator.
Sheepy
1
@Sergio Tidak pendek, tapi ini adalah ekspresi tunggal, yang membuat perbedaan besar.
michaelsnowden
5
List result = new ArrayList();
while (i.hasNext()){
    result.add(i.next());
}
Akvel
sumber
11
@LuggiMendoza: Stack Overflow tidak dimaksudkan sebagai tempat di mana Anda bisa memotong dan menempel dan menyelesaikan masalah Anda. Ini dimaksudkan untuk menjadi informatif. Ini adalah jawaban yang sangat informatif dan setiap orang yang masuk akal harus dapat menyatukannya bahwa saya adalah seorang iterator. Dari bunyi-bunyiannya, Anda hampir tidak berusaha untuk memahami apa yang sedang terjadi.
CaTalyst.X
2

Coba StickyListdari Cactoos :

List<String> list = new StickyList<>(iterable);

Penafian: Saya salah satu pengembang.

yegor256
sumber
Ini tidak menjawab pertanyaan. Iterable! =Iterator
xehpuk
Hebat, sekarang Anda mungkin perlu membuat JavaDoc untuk versi baru dan memperbarui tautan. :)
xehpuk
2

Berikut ini adalah one-liner menggunakan Streams

Iterator<?> iterator = ...
List<?> list = StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false)
                .collect(Collectors.toList());
apflieger
sumber
1

Saya hanya ingin menunjukkan solusi yang tampaknya jelas yang TIDAK akan berhasil:

Daftar daftar = Stream.generate (iterator :: next)
    .collect (Collectors.toList ());

Itu karena Stream#generate(Supplier<T>)hanya dapat membuat aliran tanpa batas, ia tidak mengharapkan argumennya untuk dibuang NoSuchElementException(itulah yang Iterator#next()akan dilakukan pada akhirnya).

Jawaban xehpuk seharusnya digunakan jika Iterator → Stream → Cara daftar adalah pilihan Anda.

Sasha
sumber
0

gunakan google jambu !

Iterable<String> fieldsIterable = ...
List<String> fields = Lists.newArrayList(fieldsIterable);

++

fedevo
sumber
Iterator (bukan Iterable) ke ArrayList
Chthonic Project
-2

Di sini dalam hal ini jika Anda ingin cara tercepat mungkin for looplebih baik.

Iterator di atas ukuran sampel 10,000 runsmembutuhkan 40 mstempat untuk loop2 ms

        ArrayList<String> alist = new ArrayList<String>();  
        long start, end;  

        for (int i = 0; i < 1000000; i++) {  
            alist.add(String.valueOf(i));  
        }  

        ListIterator<String> it = alist.listIterator();      

        start = System.currentTimeMillis();  
        while (it.hasNext()) {  
            String s = it.next();  
        }  
        end = System.currentTimeMillis();  

        System.out.println("Iterator start: " + start + ", end: " + end + ", delta: "  
            + (end - start));  
        start = System.currentTimeMillis();  
        int ixx = 0;  
        for (int i = 0; i < 100000; i++) {  
            String s = alist.get(i);  
        }  

        System.out.println(ixx);  
        end = System.currentTimeMillis();  
        System.out.println("for loop start: " + start + ", end: " + end + ", delta: "  
            + (end - start));  

Itu dengan asumsi bahwa daftar tersebut berisi string.

vikiiii
sumber
3
Tentunya menggunakan forloop dan mengakses elemen daftar dengan get(i)lebih cepat daripada menggunakan iterator ... tapi bukan itu yang ditanyakan OP, ia secara khusus menyebutkan bahwa iterator diberikan sebagai input.
Óscar López
@Oscar saya minta maaf. Haruskah saya menghapus jawaban saya?
vikiiii
Itu panggilanmu. Saya belum menghapusnya, mungkin informatif. Saya hanya menghapus jawaban saya ketika orang-orang mulai mengurungkannya :)
Óscar López
Saya pikir @ ÓscarLópez benar ... ini mungkin bukan jawaban yang diperlukan, tetapi berisi informasi yang berguna bagi pembaca (seperti saya: P).
Proyek Chthonic
Anda memiliki bug dalam jawaban Anda. Dalam get(int)loop Anda hanya melihat 100k dari entri 1m. Jika Anda mengubah loop itu menjadi it.size()Anda akan melihat bahwa 2 metode ini dekat dengan kecepatan yang sama. Secara umum tes kecepatan java semacam ini memberikan informasi kinerja kehidupan nyata yang terbatas dan harus dilihat secara skeptis.
Gray