Yineleyiciyi ArrayList'e Dönüştür


241

Verilen Iterator<Element>, bunu dönüştürmek nasıl Iteratorüzere ArrayList<Element>(veya List<Element>içinde) en iyi ve en hızlı şekilde mümkün, bu yüzden kullanabileceği ArrayListbu gibi üzerinde s' operasyonları get(index), add(element)vb

Yanıtlar:


360

Guava gibi bir kütüphaneyi daha iyi kullanın :

import com.google.common.collect.Lists;

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

Başka bir Guava örneği:

ImmutableList.copyOf(myIterator);

veya Apache Commons Koleksiyonları :

import org.apache.commons.collections.IteratorUtils;

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

List<Element> myList = IteratorUtils.toList(myIterator);       

8
Anlamıyorum. ArrayList, örneğin Guava tarafından normal bir ArrayList'ten daha iyi bir şekilde ne şekilde döndürülür? Bunu daha verimli bir şekilde yapıyorlar mı? Daha verimli olsa bile, projenize ekstra bir bağımlılık (ve daha karmaşıklık) eklemeye değer mi?
CorayThan

10
@CorayThan daha az kod + test edilmiş yöntemler. Seninle hemfikir olsam da, sadece bu yöntemi kullanmak için ekstra bir bağımlılık eklemem. Ama sonra tekrar, (büyük) projelerimin çoğunda ya Guava ya da Apache Commons kullanılıyor ...
Renaud

4
@CorayThan Arkadaşımı böl ve fethet. Neden zaten bir kütüphane tarafından sağlanan ve test edilen bir yöntem yazmalısınız? Çok fazla Apache Commons ve Guava kullanıyoruz, sadece harikalar ve zamandan ve paradan tasarruf etmenize yardımcı oluyorlar.
Stephan

5
Renaud ve Stephan ile aynı fikirde. Ayrıca, bir derleme aracı kullanıyorsanız, bu kütüphaneleri dahil etmek dünyanın en kolay şeyidir ... AYRICA ... gerçekten dahil etmek istemiyorsanız Apache / Guava kodunun kaynağına gidebilirsiniz ve orada yaptıklarını kopyala: Zaten orada olan yüzlerce güzel tasarlanmış ve test edilmiş tekerleği yeniden icat ederek zamanını boşa harcamaktan daha iyi.
mike kemirgen

238

Java 8'de, arabirime forEachRemainingeklenen yeni yöntemi kullanabilirsiniz Iterator:

List<Element> list = new ArrayList<>();
iterator.forEachRemaining(list::add);

2
Ne demek ::? hangi adı var? list.add () öğesine doğrudan bir başvuru gibi görünüyor; ve java8 yeni bir şey gibi görünüyor; ve teşekkürler! :)
Kova Gücü

13
@AquariusPower ::Sözdizimi Java 8'de yenidir ve lambda'nın kestirme bir biçimi olan bir "yöntem başvurusu" anlamına gelir. Daha fazla bilgi için buraya bakın: docs.oracle.com/javase/tutorial/java/javaOO/…
Stuart Marks

nereden iteratorgeliyor çözülmemiş bir semboldür
javadba

1
@javadba Orijinal soru, zaten sahip olduğunuz bir yineleyiciyi yeni bir ArrayList'e nasıl dönüştüreceğinizle ilgiliydi. Bu örneğin amaçları için, zaten sahip olduğunuz yineleyicinin çağrıldığı varsayılır iterator.
Stuart Marks

1
Bu, en kısa olduğu ve 3. parti kütüphanelerine bağlı olmadığı için kabul edilen cevap olmalıdır
DanielCuadra

64

Bir yineleyiciyi aşağıdaki gibi yeni bir listeye kopyalayabilirsiniz:

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

Bu, listenin dize içerdiğini varsayar. Bir listeyi bir yineleyiciden yeniden oluşturmanın daha hızlı bir yolu yoktur, elle gezdirmek ve her bir öğeyi uygun türden yeni bir listeye kopyalamak zorunda kalırsınız.

DÜZENLE :

Aşağıda, yineleyiciyi yeni bir listeye güvenli bir şekilde kopyalamak için genel bir yöntem verilmiştir:

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

Şöyle kullanın:

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]

26

IterableVe arasında bir fark olduğunu unutmayın Iterator.

Bir paranız varsa Iterable, Java 8 ile bu çözümü kullanabilirsiniz:

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

Bildiğim gibi örnek Collectors.toList()oluşturur ArrayList.

Aslında bence, bir satırda da iyi görünüyor.
Örneğin List<Element>, bazı yöntemlerden geri dönmeniz gerekiyorsa :

return StreamSupport.stream(iter.spliterator(), false).collect(Collectors.toList());

4
Soru, Yineleyici <Element> değil, başlangıç ​​noktası olarak Yineleyici <Element> ile ilgilidir.
Jaap

1
yukarıdaki yoruma katılıyorum. süper kafa karıştırıcı adını verdi Iterable iterator. Bunlar tamamen farklı.
Stephen Harrison

Java 8'de kolayca bir dönüştürebilirsiniz Iteratorbir karşı arkasını tek kullanım Iterable kullanarak () -> iterator. Bu, bu gibi durumlarda yararlı olabilir, ancak önemli şeyi tekrar vurgulamama izin verin: Bu yöntemi yalnızca tek bir kullanım Iterable kabul edilebilirse kullanabilirsiniz . iterable.iterator()Birden fazla çağrı yapmak beklenmedik sonuçlar verecektir. Yukarıdaki cevap daha sonraIterator<Element> iterator = createIterator(); List<Element> array = StreamSupport.stream(((Iterable<Element>) () -> iterable).spliterator(), false).collect(toList());
neXus


9

Sade Java 8 ile oldukça özlü bir çözüm 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)
    );
}

Bu akışı api kullanarak yazmak için daha kompakt bir yolu var mı? Normal while döngüsü yolundan daha basit görünmüyor.
xi.lin

37
Bu çözüme "özlü" demem.
Sergio

1
@Sergio Bu yüzden "güzel" yazdım. Ancak, yerel değişkenlere ve sadece bir noktalı virgüle ihtiyaç duymaz. Statik içe aktarmalarla kısaltabilirsiniz.
xehpuk

1
iterator.forEachRemaining( list::add )Java 8'de de yeni olanların çok daha özlü olduğunu söyleyebilirim . Liste değişkeninin içine Collectoritilmesi, a Streamve a tarafından desteklenmesi gerektiğinden, bu durumda okunabilirliği artırmaz Spliterator.
Sheepy

1
@Sergio Kısa değil, ama çok büyük bir fark yaratan tek bir ifade.
michaelsnowden

5
List result = new ArrayList();
while (i.hasNext()){
    result.add(i.next());
}

11
@LuggiMendoza: Yığın Taşması, sorununuzu yalnızca kesip yapıştırabileceğiniz ve çözebileceğiniz bir yer değildir. Bilgilendirici olması amaçlanmıştır. Bu mükemmel bilgilendirici bir cevaptır ve makul bir kişi i'nin bir yineleyici olduğunu bir araya getirebilmelidir. Seslerinden, neler olup bittiğini anlamaya çalışmak için neredeyse hiç çaba sarf etmiyorsunuz.
CaTalyst.X

2

Deneyin StickyListgelen Cactoos :

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

Feragatname: Geliştiricilerden biriyim.


Bu soruya cevap vermiyor. Iterable! =Iterator
xehpuk

Harika, şimdi muhtemelen yeni sürüm için JavaDoc oluşturmanız ve bağlantıyı güncellemeniz gerekiyor. :)
xehpuk

2

İşte Streams kullanan bir astar

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


0

google guava kullanın !

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

++


ArrayList'e Yineleyici Değil (
Yinelenemez

-2

Burada bu durumda mümkün olan en hızlı yolu istiyorsanız o zaman for loopdaha iyidir.

Bir numune sayısı üzerinden yineleyici 10,000 runsalır 40 msdöngü alır için gibi olduğu2 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));  

Bu, listenin dize içerdiğini varsayar.


3
Elbette bir fordöngü kullanmak ve bir listenin öğelerine erişmek get(i)bir yineleyici kullanmaktan daha hızlıdır ... ancak OP'nin istediği bu değildi, özellikle bir yineleyicinin girdi olarak verildiğini belirtti .
Lópezscar López

@ Özür dilerim, cevabımı silebilir miyim?
vikiiii

Bu senin çağrın. Henüz silmem, bilgilendirici olabilir. Yanıtlarımı yalnızca insanlar onları aşağı indirmeye başladığında siliyorum :)
López

@ ÓscarLópez haklı olduğunu düşünüyorum ... bu gerekli cevap olmayabilir, ama okuyucular için yararlı bilgiler içeriyor (benim gibi: P).
Chthonic Projesi

Cevabınızda bir hata var. Gelen get(int)döngü yalnızca 1m girişlerinin 100k bakıyoruz. Bu döngüyü değiştirirseniz, it.size()bu 2 yöntemin aynı hıza yakın olduğunu görürsünüz. Genel olarak bu tür java hız testleri sınırlı gerçek yaşam performansı bilgileri sağlar ve şüpheyle bakılmalıdır.
Gri
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.