Convertir l'itérateur en ArrayList

241

Étant donné Iterator<Element>, comment pouvons - nous le convertir Iteratorà ArrayList<Element>(ou List<Element>) dans le meilleur et le plus rapide façon possible, afin que nous puissions utiliser les ArrayListopérations de sur elle, comme get(index), add(element), etc.

Maksim
la source

Réponses:

360

Mieux vaut utiliser une bibliothèque comme Guava :

import com.google.common.collect.Lists;

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

Un autre exemple de goyave:

ImmutableList.copyOf(myIterator);

ou Collections Apache Commons :

import org.apache.commons.collections.IteratorUtils;

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

List<Element> myList = IteratorUtils.toList(myIterator);       
Renaud
la source
8
Je ne comprends pas. De quelle manière la ArrayList est-elle renvoyée, disons, par Guava mieux qu'une ArrayList normale? Le font-ils de manière plus efficace? Même s'il est plus efficace, vaut-il vraiment la peine d'ajouter une dépendance supplémentaire (et plus de complexité) à votre projet?
CorayThan
10
@CorayThan moins de code + méthodes testées. Bien que je sois d'accord avec vous, je n'ajouterais pas de dépendance supplémentaire uniquement pour utiliser cette méthode. Mais là encore, la plupart de mes (gros) projets utilisent Guava ou Apache Commons ...
Renaud
4
@CorayThan Divisez et conquérez mon ami. Pourquoi écrire une méthode déjà fournie par une bibliothèque et testée? Nous utilisons beaucoup d'Apache Commons et de Guava, ils sont tout simplement géniaux et vous aident à économiser du temps et de l'argent.
Stephan
5
D'accord avec Renaud et Stephan. De plus, si vous utilisez un outil de construction, c'est la chose la plus simple au monde pour inclure ces bibliothèques ... AUSSI ... si vous ne voulez vraiment pas les inclure, vous pouvez aller à la source du code Apache / Guava et copiez ce qu'ils ont fait là-bas: bien mieux que de perdre votre temps à réinventer les centaines de roues magnifiquement conçues et testées qui existent déjà.
mike rodent
238

Dans Java 8, vous pouvez utiliser la nouvelle forEachRemainingméthode qui a été ajoutée à l' Iteratorinterface:

List<Element> list = new ArrayList<>();
iterator.forEachRemaining(list::add);
Stuart Marks
la source
2
qu'est-ce que cela signifie ::? quel nom a-t-il? semble une référence directe à list.add (); et semble aussi quelque chose de nouveau java8; et merci! :)
Aquarius Power
13
@AquariusPower La ::syntaxe est nouvelle dans Java 8 et fait référence à une "référence de méthode", qui est une forme abrégée d'un lambda. Voir ici pour plus d'informations: docs.oracle.com/javase/tutorial/java/javaOO/…
Stuart Marks
d'où iteratorvient-il? c'est un symbole non résolu
javadba
1
@javadba La question initiale était de savoir comment convertir un itérateur que vous avez déjà en une nouvelle ArrayList. Pour les besoins de cet exemple, l'itérateur que vous possédez déjà est supposé être appelé iterator.
Stuart Marks
1
Cela devrait être la réponse acceptée, car elle est la plus courte et ne dépend pas des bibliothèques tierces
DanielCuadra
64

Vous pouvez copier un itérateur dans une nouvelle liste comme celle-ci:

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

Cela suppose que la liste contient des chaînes. Il n'y a vraiment pas de moyen plus rapide de recréer une liste à partir d'un itérateur, vous êtes obligé de le parcourir à la main et de copier chaque élément dans une nouvelle liste du type approprié.

ÉDITER :

Voici une méthode générique pour copier un itérateur dans une nouvelle liste de manière sécurisée:

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

Utilisez-le comme ceci:

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
la source
26

Notez qu'il y a une différence entre Iterableet Iterator.

Si vous en avez un Iterable, alors avec Java 8, vous pouvez utiliser cette solution:

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

Comme je sais, Collectors.toList()crée une ArrayListinstance.

En fait, à mon avis, cela semble également bien sur une seule ligne.
Par exemple, si vous devez revenir List<Element>d'une méthode:

return StreamSupport.stream(iter.spliterator(), false).collect(Collectors.toList());
Dub Nazar
la source
4
La question concerne Iterator <Element> comme point de départ, et non Iterable <Element>.
Jaap
1
d'accord avec le commentaire ci-dessus. super confus que vous avez nommé votre Iterable iterator. Ils sont complètement différents.
Stephen Harrison
Dans Java 8, vous pouvez facilement convertir un Iteratordos en une seule utilisation Iterable en utilisant () -> iterator. Cela peut être utile dans des situations comme celle-ci, mais permettez-moi de souligner à nouveau la chose importante: vous ne pouvez utiliser cette méthode que si une seule utilisation Iterable est acceptable. Appeler iterable.iterator()plus d'une fois donnera des résultats inattendus. La réponse ci-dessus devient alorsIterator<Element> iterator = createIterator(); List<Element> array = StreamSupport.stream(((Iterable<Element>) () -> iterable).spliterator(), false).collect(toList());
neXus
9

Solution assez concise avec Java 8 simple en utilisant 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
la source
Existe-t-il un moyen plus compact d'écrire cela en utilisant l'API Stream? Cela ne semble pas plus simple que la boucle normale.
xi.lin
37
Je n'appellerais pas cette solution "concise".
Sergio
1
@Sergio C'est pourquoi j'ai écrit "jolie". Cependant, il n'a pas besoin de variables locales et d'un seul point-virgule. Vous pouvez le raccourcir avec des importations statiques.
xehpuk
1
Je dirais iterator.forEachRemaining( list::add ), également nouveau dans Java 8, est beaucoup plus concis. Pousser la variable de liste dans Collectorn'améliore pas la lisibilité dans ce cas, car elle doit être prise en charge par a Streamet a Spliterator.
Sheepy
1
@Sergio Ce n'est pas court, mais c'est une seule expression, ce qui fait une énorme différence.
michaelsnowden
5
List result = new ArrayList();
while (i.hasNext()){
    result.add(i.next());
}
Akvel
la source
11
@LuggiMendoza: Stack Overflow n'est pas censé être un endroit où vous pouvez simplement couper-coller et résoudre votre problème. Il se veut informatif. Il s'agit d'une réponse parfaitement informative et toute personne raisonnable devrait être en mesure de comprendre que le i était un itérateur. À en juger par les sons, vous ne faites presque aucun effort pour essayer de comprendre ce qui se passait.
CaTalyst.X
2

Essayez StickyListavec Cactoos :

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

Avertissement: je suis l'un des développeurs.

yegor256
la source
Cela ne répond pas à la question. Iterable! =Iterator
xehpuk
Génial, maintenant vous devez probablement générer JavaDoc pour la nouvelle version et mettre à jour le lien. :)
xehpuk
2

Voici une ligne à l'aide de Streams

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

Je veux juste souligner une solution apparemment évidente qui ne sera PAS fonctionnera :

Liste liste = Stream.generate (itérateur :: suivant)
    .collect (Collectors.toList ());

C'est parce que Stream#generate(Supplier<T>)ne peut créer que des flux infinis, il ne s'attend pas à ce que son argument lance NoSuchElementException(c'est ce queIterator#next() qui fera à la fin).

La réponse du xehpuk doit être utilisée à la place si la méthode Iterator → Stream → List est votre choix.

Sasha
la source
0

utilisez google guava !

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

++

fedevo
la source
Iterator (not Iterable) to ArrayList
Chthonic Project
-2

Ici, dans ce cas, si vous voulez le moyen le plus rapide possible, for loopc'est mieux.

L'itérateur sur une taille d'échantillon de 10,000 runsprises 40 msoù comme pour les boucles2 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));  

Cela suppose que la liste contient des chaînes.

vikiiii
la source
3
Certes, utiliser une forboucle et accéder aux éléments d'une liste get(i)est plus rapide que d'utiliser un itérateur ... mais ce n'est pas ce que le PO demandait, il a spécifiquement mentionné qu'un itérateur est donné en entrée.
Óscar López
@Oscar je suis désolé. Dois-je supprimer ma réponse?
vikiiii
Voilà votre appel. Je ne le supprimerais pas encore, cela pourrait être informatif. Je ne supprime mes réponses que lorsque les gens commencent à les voter :)
Óscar López
Je pense que @ ÓscarLópez a raison ... ce n'est peut-être pas la réponse requise, mais elle contient des informations utiles pour les lecteurs (comme moi: P).
Chthonic Project
Vous avez un bug dans votre réponse. Dans la get(int)boucle, vous ne regardez que 100k des entrées de 1m. Si vous changez cette boucle pour être, it.size()vous verrez que ces 2 méthodes sont proches de la même vitesse. En général, ces types de tests de vitesse java fournissent des informations limitées sur les performances réelles et doivent être examinés avec scepticisme.
Gray