Converti Iterator in ArrayList


241

Dato Iterator<Element>, come possiamo convertire tale Iteratora ArrayList<Element>(o List<Element>) nella migliore e più veloce modo possibile, in modo che possiamo usare ArrayLists' operazioni su di esso, come get(index), add(element)ecc

Risposte:


360

Meglio usare una libreria come Guava :

import com.google.common.collect.Lists;

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

Un altro esempio di Guava:

ImmutableList.copyOf(myIterator);

o collezioni Apache Commons :

import org.apache.commons.collections.IteratorUtils;

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

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

8
Non capisco In che modo ArrayList viene restituito, ad esempio, da Guava meglio di un normale ArrayList? Lo fanno in un modo più efficiente? Anche se è più efficiente, vale davvero la pena aggiungere una dipendenza aggiuntiva (e più complessità) al tuo progetto?
Coray:

10
@Coray: meno codice + metodi testati. Sebbene sia d'accordo con te, non aggiungerei una dipendenza aggiuntiva solo per usare quel metodo. Ma ancora una volta, la maggior parte dei miei (grandi) progetti usa Guava o Apache Commons ...
Renaud

4
@CorayThan Dividi e conquista il mio amico. Perché scrivere un metodo che è già fornito da una libreria ed è testato? Stiamo usando molti Apache Commons e Guava, sono semplicemente fantastici e ti aiutano a risparmiare tempo e denaro.
Stephan,

5
Concordo con Renaud e Stephan. Inoltre, se stai usando uno strumento di compilazione è la cosa più semplice al mondo includere queste librerie ... ANCHE ... se davvero non vuoi includerle puoi andare alla fonte del codice Apache / Guava e copia quello che hanno fatto lì: LONTANO meglio di perdere tempo reinventando le centinaia di ruote ben progettate e testate che sono già là fuori.
Mike Rodent,

238

In Java 8, è possibile utilizzare il nuovo forEachRemainingmetodo che è stato aggiunto Iteratorall'interfaccia:

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

2
cosa significa ::? che nome ha? sembra un riferimento diretto a list.add (); e sembra anche qualcosa di nuovo di java8; e grazie! :)
Aquarius Power

13
@AquariusPower La ::sintassi è nuova in Java 8 e si riferisce a un "metodo di riferimento", che è una forma abbreviata di un lambda. Vedi qui per ulteriori informazioni: docs.oracle.com/javase/tutorial/java/javaOO/…
Stuart Marks

da dove viene iterator? è un simbolo irrisolto
Javavba il

1
@javadba La domanda originale era su come convertire un iteratore che hai già in un nuovo ArrayList. Ai fini di questo esempio, si presume che sia chiamato l'iteratore che hai già iterator.
Stuart segna il

1
Questa dovrebbe essere la risposta accettata, poiché è la più breve e non dipende da librerie di terze parti
DanielCuadra,

64

Puoi copiare un iteratore in un nuovo elenco come questo:

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

Ciò presuppone che l'elenco contenga stringhe. Non c'è davvero un modo più veloce per ricreare un elenco da un iteratore, sei bloccato nel attraversarlo a mano e copiare ogni elemento in un nuovo elenco del tipo appropriato.

MODIFICARE :

Ecco un metodo generico per copiare un iteratore in un nuovo elenco in modo sicuro:

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

Usalo in questo modo:

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

Nota che c'è una differenza tra Iterablee Iterator.

Se ne hai uno Iterable, allora con Java 8 puoi usare questa soluzione:

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

Come so Collectors.toList()crea ArrayLististanza.

In realtà a mio avviso, sembra anche buono in una riga.
Ad esempio se è necessario tornare List<Element>da un metodo:

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

4
La domanda riguarda Iterator <Element> come punto di partenza, non Iterable <Element>.
Jaap,

1
d'accordo con il commento sopra. super confuso che hai chiamato il tuo Iterable iterator. Sono completamente diversi.
Stephen Harrison,

In Java 8 è possibile convertire facilmente un Iteratornuovo ad un singolo uso Iterable utilizzando () -> iterator. Questo può essere utile in situazioni come questa, ma vorrei sottolineare ancora una volta la cosa importante: puoi usare questo metodo solo se monouso Iterable è accettabile. Chiamare iterable.iterator()più di una volta produrrà risultati inaspettati. La risposta di cui sopra diventa quindiIterator<Element> iterator = createIterator(); List<Element> array = StreamSupport.stream(((Iterable<Element>) () -> iterable).spliterator(), false).collect(toList());
neXus l'


9

Soluzione piuttosto concisa con Java 8 semplice usando 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)
    );
}

C'è un modo più compatto per scrivere questo usando stream api? Non sembra più semplice del normale ciclo while.
xi.lin,

37
Non definirei questa soluzione "concisa".
Sergio,

1
@Sergio Ecco perché ho scritto "carino". Tuttavia, non necessita di variabili locali e solo un punto e virgola. È possibile accorciarlo con importazioni statiche.
xehpuk,

1
Direi che iterator.forEachRemaining( list::add ), anche nuovo in Java 8, è molto più conciso. L'inserimento della variabile elenco in Collectornon migliora la leggibilità in questo caso, poiché deve essere supportato da a Streame a Spliterator.
Sheepy,

1
@Sergio Non è breve, ma è una singola espressione, che fa una grande differenza.
michaelsnowden,

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

11
@LuggiMendoza: Stack Overflow non è pensato per essere un posto dove puoi semplicemente tagliare e incollare e risolvere il tuo problema. È pensato per essere informativo. Questa è una risposta perfettamente informativa e ogni persona ragionevole dovrebbe essere in grado di mettere insieme che io ero un iteratore. Dal suono di ciò, non hai fatto quasi nessuno sforzo per cercare di capire cosa stesse succedendo.
CaTalyst.X il

2

Prova StickyListda Cactoos :

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

Disclaimer: sono uno degli sviluppatori.


Questo non risponde alla domanda. Iterable! =Iterator
xehpuk

Bene, ora probabilmente dovrai generare JavaDoc per la nuova versione e aggiornare il link. :)
xehpuk

2

Ecco un one-liner che utilizza Stream

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

1

Voglio solo sottolineare una soluzione apparentemente ovvia che NON funzionerà:

Elenco elenco = Stream.generate (iteratore :: successivo)
    .Raccogliere (Collectors.toList ());

Questo perché Stream#generate(Supplier<T>)può creare solo flussi infiniti, non si aspetta che il suo argomento venga lanciato NoSuchElementException(questo è ciò Iterator#next()che farà alla fine).

La risposta di xehpuk dovrebbe essere usata invece se il modo Iteratore → Stream → Elenco è la tua scelta.


0

usa google guava !

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

++


Iteratore (non Iterable) in ArrayList
Chthonic Project

-2

Qui in questo caso, se si desidera il modo più veloce possibile, allora for loopè meglio.

L'iteratore su una dimensione del campione di 10,000 runstake prende 40 mscome per 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));  

Ciò presuppone che l'elenco contenga stringhe.


3
Sicuramente usando a for loop e accedere agli elementi di una lista get(i)è più veloce dell'uso di un iteratore ... ma non è quello che l'OP stava chiedendo, ha specificamente menzionato che un iteratore è dato come input.
Óscar López,

@Oscar mi dispiace. Devo eliminare la mia risposta?
vikiiii,

Questa è la tua chiamata. Non lo cancellerei ancora, potrebbe essere informativo. Elimina le mie risposte solo quando le persone iniziano a votarle :)
Óscar López

Penso che @ ÓscarLópez abbia ragione ... questa potrebbe non essere la risposta richiesta, ma contiene informazioni utili per i lettori (come me: P).
Chthonic Project,

Hai un bug nella tua risposta. Nel get(int)loop stai guardando solo 100k delle voci da 1m. Se cambi quel loop per essere it.size()vedrai che questi 2 metodi sono vicini alla stessa velocità. In generale, questi tipi di test di velocità Java forniscono informazioni limitate sulle prestazioni della vita reale e dovrebbero essere considerati scettici.
Gray
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.