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.

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);       

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);

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]

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());

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)
    );
}

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());
}

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.


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());

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.


0

utilisez google guava !

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

++


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.


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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.