Conversão Iterator para ArrayList


241

Dado Iterator<Element>, como podemos converter que Iteratora ArrayList<Element>(ou List<Element>) na melhor e mais rápida maneira possível, de modo que podemos usar ArrayList's operações nele, como get(index), add(element), etc.

Respostas:


360

Melhor usar uma biblioteca como o Guava :

import com.google.common.collect.Lists;

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

Outro exemplo de goiaba:

ImmutableList.copyOf(myIterator);

ou Apache Commons Collections :

import org.apache.commons.collections.IteratorUtils;

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

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

8
Eu não entendo. De que maneira o ArrayList é retornado por, digamos, Goiaba melhor do que um ArrayList normal? Eles fazem isso de uma maneira mais eficiente? Mesmo se for mais eficiente, vale a pena adicionar uma dependência extra (e mais complexidade) ao seu projeto?
CorayThan

10
@CorayThan menos código + métodos testados. Embora eu concorde com você, não adicionaria uma dependência extra apenas para usar esse método. Mas, novamente, a maioria dos meus (grandes) projectos usar goiaba ou Apache Commons ...
Renaud

4
@CorayThan Divida e conquiste meu amigo. Por que escrever um método que já é fornecido por uma biblioteca e é testado? Estamos usando muitos Apache Commons e Guava, eles são incríveis e ajudam você a economizar tempo e dinheiro.
Stephan

5
Concordo com Renaud e Stephan. Além disso, se você estiver usando uma ferramenta de construção, é a coisa mais fácil do mundo incluir essas bibliotecas ... TAMBÉM ... se você realmente não quiser incluí-las, poderá acessar a fonte do código Apache / Guava e copie o que eles fizeram lá: MUITO melhor do que desperdiçar seu tempo reinventando as centenas de rodas lindamente projetadas e testadas que já estão lá fora.
mike roedor

238

No Java 8, você pode usar o novo forEachRemainingmétodo que foi adicionado à Iteratorinterface:

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

2
o que significa ::? que nome tem isso? parece uma referência direta a list.add (); e parece também alguma coisa nova em java8; e obrigado! :)
Poder de Aquário

13
@AquariusPower A ::sintaxe é nova no Java 8 e refere-se a uma "referência de método", que é uma forma abreviada de uma lambda. Veja aqui para obter mais informações: docs.oracle.com/javase/tutorial/java/javaOO/…
Stuart Marks

de onde iteratorvem? é um símbolo não resolvido
javadba

1
@javadba A pergunta original era sobre como converter um iterador que você já possui em um novo ArrayList. Para os propósitos deste exemplo, presume-se que o iterador que você já possui seja chamado iterator.
Stuart Marks

1
Esta deve ser a resposta aceita, como é o mais curto e não depende de bibliotecas de 3rd party
DanielCuadra

64

Você pode copiar um iterador para uma nova lista como esta:

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

Isso pressupõe que a lista contenha cadeias. Realmente não existe uma maneira mais rápida de recriar uma lista a partir de um iterador, você deve atravessá-la manualmente e copiar cada elemento para uma nova lista do tipo apropriado.

EDIT:

Aqui está um método genérico para copiar um iterador para uma nova lista de uma maneira segura para o tipo:

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

Use-o assim:

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

Observe que há uma diferença entre Iterablee Iterator.

Se você possui um Iterable, então com o Java 8 você pode usar esta solução:

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

Como eu sei Collectors.toList()cria ArrayListinstância.

Na verdade, na minha opinião, também fica bem em uma linha.
Por exemplo, se você precisar retornar List<Element>de algum método:

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

4
A questão é sobre o Iterador <Element> como ponto de partida, não o Iterável <Element>.
21417 Jaap

1
concorda com o comentário acima. super confuso que você nomeou seu Iterable iterator. Eles são completamente diferentes.
Stephen Harrison

No Java 8, você pode facilmente converter uma Iteratorvolta para um único uso Iterable usando () -> iterator. Isso pode ser útil em situações como essa, mas deixe-me enfatizar o importante novamente: você pode usar esse método apenas se um único uso Iterable for aceitável. Ligar iterable.iterator()mais de uma vez produzirá resultados inesperados. A resposta acima é entãoIterator<Element> iterator = createIterator(); List<Element> array = StreamSupport.stream(((Iterable<Element>) () -> iterable).spliterator(), false).collect(toList());
neXus


9

Solução bastante concisa com Java 8 simples 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)
    );
}

Existe uma maneira mais compacta de escrever isso usando a API de fluxo? Parece não ser mais simples que o normal, enquanto o loop while.
xi.lin

37
Eu não chamaria essa solução de "concisa".
1516 Sergio Sergio

1
@Sergio É por isso que escrevi "bonita". No entanto, ele não precisa de variáveis ​​locais e apenas um ponto e vírgula. Você pode reduzi-lo com importações estáticas.
xehpuk

1
Eu diria iterator.forEachRemaining( list::add ), também novo no Java 8, é muito mais conciso. Pressionar a variável da lista para dentro Collectornão melhora a legibilidade nesse caso, pois ela precisa ser suportada por a Streame a Spliterator.
Sheepy

1
@Sergio Não é curto, mas é uma expressão única, o que faz uma enorme diferença.
michaelsnowden

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

11
@LuggiMendoza: Stack Overflow não é para ser um lugar onde você pode simplesmente cortar e colar e resolver seu problema. É para ser informativo. Esta é uma resposta perfeitamente informativa e qualquer pessoa razoável deve ser capaz de concluir que o i era um iterador. Pelo que parece, você não faz quase nenhum esforço para tentar entender o que estava acontecendo.
CaTalyst.X

2

Experimente StickyListno Cactoos :

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

Disclaimer: Eu sou um dos desenvolvedores.


Isso não responde à pergunta. Iterable! =Iterator
xehpuk

Ótimo, agora você provavelmente precisa gerar o JavaDoc para a nova versão e atualizar o link. :)
xehpuk 24/10

2

Aqui está uma lista usando Streams

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


0

use o google goiaba !

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

++


Iterador (não Iterável) para ArrayList
Chthonic Project

-2

Aqui, neste caso, se você quiser o caminho mais rápido possível, então for loopé melhor.

O iterador sobre um tamanho de amostra de 10,000 runstakes 40 msonde, quanto ao loop, leva2 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));  

Isso pressupõe que a lista contenha cadeias.


3
Certamente, usar um forloop e acessar os elementos de uma lista get(i)é mais rápido do que usar um iterador ... mas não era isso que o OP estava pedindo, ele mencionou especificamente que um iterador é fornecido como entrada.
Óscar López

@Oscar me desculpe. Devo excluir minha resposta?
vikiiii

Essa é a sua decisão. Eu não o excluiria ainda, pode ser informativo. Eu só apagar minhas respostas quando as pessoas começam a downvote eles :)
Óscar López

Acho que @ ÓscarLópez está certo ... essa pode não ser a resposta necessária, mas contém informações úteis para os leitores (como eu: P).
Chthonic Project

Você tem um erro na sua resposta. No get(int)loop, você está olhando apenas 100k das entradas de 1m. Se você alterar esse loop it.size(), verá que esses dois métodos estão próximos da mesma velocidade. Em geral, esses tipos de testes de velocidade do java fornecem informações limitadas sobre o desempenho na vida real e devem ser vistos com ceticismo.
Grey
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.