Konvertieren Sie Iterator in ArrayList

241

In Anbetracht Iterator<Element>, wie können wir das konvertieren Iteratorzu ArrayList<Element>(oder List<Element>) in der besten und schnellsten Art und Weise möglich, dass so können wir verwenden ArrayList‚s Operationen darauf, wie get(index), add(element)etc.

Maksim
quelle

Antworten:

360

Verwenden Sie besser eine Bibliothek wie Guava :

import com.google.common.collect.Lists;

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

Ein weiteres Guaven-Beispiel:

ImmutableList.copyOf(myIterator);

oder Apache Commons Sammlungen :

import org.apache.commons.collections.IteratorUtils;

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

List<Element> myList = IteratorUtils.toList(myIterator);       
Renaud
quelle
8
Ich verstehe es nicht Inwiefern wird die ArrayList beispielsweise von Guava besser zurückgegeben als eine normale ArrayList? Tun sie es effizienter? Auch wenn es effizienter ist, lohnt es sich wirklich, Ihrem Projekt eine zusätzliche Abhängigkeit (und Komplexität) hinzuzufügen?
CorayThan
10
@CorayThan weniger Code + getestete Methoden. Obwohl ich Ihnen zustimme, würde ich keine zusätzliche Abhängigkeit hinzufügen, nur um diese Methode zu verwenden. Aber andererseits verwenden die meisten meiner (großen) Projekte entweder Guava oder Apache Commons ...
Renaud
4
@CorayThan Teile und erobere meinen Freund. Warum eine Methode schreiben, die bereits von einer Bibliothek bereitgestellt und getestet wird? Wir verwenden viele Apache Commons und Guava. Sie sind einfach fantastisch und helfen Ihnen, Zeit und Geld zu sparen.
Stephan
5
Stimmen Sie mit Renaud und Stephan überein. Wenn Sie ein Build-Tool verwenden, ist es weltweit am einfachsten, diese Bibliotheken einzuschließen ... AUCH ... wenn Sie sie wirklich nicht einschließen möchten, können Sie zur Quelle von Apache / Guava-Code und gehen Kopieren Sie, was sie dort getan haben: Weitaus besser, als Ihre Zeit damit zu verschwenden, die Hunderte von wunderschön konstruierten und getesteten Rädern neu zu erfinden, die es bereits gibt.
Mike Nagetier
238

In Java 8 können Sie die neue forEachRemainingMethode verwenden, die der IteratorBenutzeroberfläche hinzugefügt wurde :

List<Element> list = new ArrayList<>();
iterator.forEachRemaining(list::add);
Stuart Marks
quelle
2
was bedeutet ::? Welchen Namen hat es? scheint ein direkter Verweis auf list.add () zu sein; und scheint auch etwas Java8 Neues zu sein; und danke! :)
Aquarius Power
13
@AquariusPower Die ::Syntax ist neu in Java 8 und bezieht sich auf eine "Methodenreferenz", bei der es sich um eine Kurzform eines Lambda handelt. Weitere Informationen finden Sie hier: docs.oracle.com/javase/tutorial/java/javaOO/…
Stuart Marks
woher kommt iteratordas es ist ein ungelöstes Symbol
javadba
1
@javadba Die ursprüngliche Frage war, wie ein bereits vorhandener Iterator in eine neue ArrayList konvertiert werden kann. In diesem Beispiel wird angenommen, dass der bereits vorhandene Iterator aufgerufen wird iterator.
Stuart Marks
1
Dies sollte die akzeptierte Antwort sein, da es die kürzeste ist und nicht von Bibliotheken von
Drittanbietern
64

Sie können einen Iterator wie folgt in eine neue Liste kopieren:

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

Dies setzt voraus, dass die Liste Zeichenfolgen enthält. Es gibt wirklich keinen schnelleren Weg, eine Liste aus einem Iterator neu zu erstellen. Sie müssen sie nicht mehr manuell durchlaufen und jedes Element in eine neue Liste des entsprechenden Typs kopieren.

EDIT:

Hier ist eine generische Methode zum typsicheren Kopieren eines Iterators in eine neue Liste:

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

Verwenden Sie es so:

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
quelle
26

Beachten Sie, dass zwischen Iterableund ein Unterschied besteht Iterator.

Wenn Sie eine haben Iterable, können Sie mit Java 8 diese Lösung verwenden:

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

Wie ich weiß, Collectors.toList()schafft ArrayListInstanz.

Eigentlich sieht es meiner Meinung nach auch in einer Zeile gut aus.
Zum Beispiel, wenn Sie List<Element>von einer Methode zurückkehren müssen:

return StreamSupport.stream(iter.spliterator(), false).collect(Collectors.toList());
Dub Nazar
quelle
4
Die Frage bezieht sich auf Iterator <Element> als Ausgangspunkt, nicht auf Iterable <Element>.
Jaap
1
stimme dem obigen Kommentar zu. super verwirrend, dass du deine benannt hast Iterable iterator. Sie sind völlig anders.
Stephen Harrison
In Java 8 können Sie ganz einfach eine konvertieren Iteratorzurück zu einem einmaligen Gebrauch Iterable durch Verwendung () -> iterator. Dies kann in solchen Situationen nützlich sein, aber lassen Sie mich noch einmal das Wichtige betonen: Sie können diese Methode nur verwenden, wenn eine einmalige Verwendung Iterable akzeptabel ist. Wenn Sie iterable.iterator()mehr als einmal anrufen , erhalten Sie unerwartete Ergebnisse. Die obige Antwort wird dannIterator<Element> iterator = createIterator(); List<Element> array = StreamSupport.stream(((Iterable<Element>) () -> iterable).spliterator(), false).collect(toList());
neXus
9

Ziemlich prägnante Lösung mit einfachem Java 8 unter Verwendung von 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
quelle
Gibt es eine kompaktere Möglichkeit, dies mit der Stream-API zu schreiben? Es scheint nicht einfacher als der normale while-Loop-Weg zu sein.
xi.lin
37
Ich würde diese Lösung nicht "prägnant" nennen.
Sergio
1
@Sergio Deshalb habe ich "hübsch" geschrieben. Es werden jedoch keine lokalen Variablen und nur ein Semikolon benötigt. Sie können es mit statischen Importen kürzen.
Xehpuk
1
Ich würde sagen iterator.forEachRemaining( list::add ), auch neu in Java 8, ist viel prägnanter. Das CollectorVerschieben der Listenvariablen in verbessert die Lesbarkeit in diesem Fall nicht, da sie von a Streamund a unterstützt werden muss Spliterator.
Sheepy
1
@Sergio Es ist nicht kurz, aber es ist ein einzelner Ausdruck, der einen großen Unterschied macht.
michaelsnowden
5
List result = new ArrayList();
while (i.hasNext()){
    result.add(i.next());
}
Akvel
quelle
11
@LuggiMendoza: Stack Overflow ist nicht als Ort gedacht, an dem Sie Ihr Problem einfach ausschneiden, einfügen und lösen können. Es soll informativ sein. Dies ist eine vollkommen informative Antwort, und jede vernünftige Person sollte in der Lage sein, zusammenzufassen, dass das i ein Iterator war. Nach den Geräuschen haben Sie fast keine Mühe darauf verwendet, zu verstehen, was los war.
CaTalyst.X
2

Versuchen StickyListvon Cactoos :

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

Haftungsausschluss: Ich bin einer der Entwickler.

yegor256
quelle
Dies beantwortet die Frage nicht. Iterable! =Iterator
xehpuk
Großartig, jetzt müssen Sie wahrscheinlich JavaDoc für die neue Version generieren und den Link aktualisieren. :)
Xehpuk
2

Hier ist ein Einzeiler mit Streams

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

Ich möchte nur auf eine scheinbar offensichtliche Lösung hinweisen, die NICHT funktioniert:

List list = Stream.generate (iterator :: next)
    .collect (Collectors.toList ());

Das liegt daran, Stream#generate(Supplier<T>)dass nur unendliche Streams erstellt werden können und nicht erwartet wird, NoSuchElementExceptiondass das Argument ausgelöst wird (das Iterator#next()wird am Ende der Fall sein ).

Die Antwort des xehpuk sollte stattdessen verwendet werden, wenn Sie den Iterator → Stream → List-Weg wählen .

Sasha
quelle
0

benutze Google Guave !

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

++

fedevo
quelle
Iterator (nicht iterierbar) zu ArrayList
Chthonic Project
-2

Hier in diesem Fall for loopist es besser, wenn Sie den schnellstmöglichen Weg wollen .

Der Iterator über eine Stichprobengröße von 10,000 runsTakes, 40 mswie für Schleifen-Takes2 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));  

Dies setzt voraus, dass die Liste Zeichenfolgen enthält.

vikiiii
quelle
3
Sicherlich ist die Verwendung einer forSchleife und der Zugriff auf die Elemente einer Liste mit get(i)schneller als die Verwendung eines Iterators ... aber das hat das OP nicht verlangt, er erwähnte ausdrücklich, dass ein Iterator als Eingabe angegeben wird.
Óscar López
@ Oscar Es tut mir leid. Soll ich meine Antwort löschen?
Vikiiii
Das ist dein Anruf. Ich würde es noch nicht löschen, es könnte informativ sein. Ich lösche meine Antworten nur, wenn Leute anfangen, sie herabzustimmen :)
Óscar López
Ich denke, @ ÓscarLópez ist richtig ... dies ist möglicherweise nicht die erforderliche Antwort, aber es enthält nützliche Informationen für Leser (wie ich: P).
Chthonic Project
Sie haben einen Fehler in Ihrer Antwort. In der get(int)Schleife sehen Sie nur 100.000 der 1-Meter-Einträge. Wenn Sie diese Schleife in ändern, werden it.size()Sie feststellen, dass diese beiden Methoden nahe an der gleichen Geschwindigkeit liegen. Im Allgemeinen liefern diese Arten von Java-Geschwindigkeitstests nur begrenzte Informationen zur tatsächlichen Leistung und sollten skeptisch betrachtet werden.
Grau