Convertir iterador a ArrayList

241

Teniendo en cuenta Iterator<Element>, ¿cómo podemos convertir eso Iteratora ArrayList<Element>(o List<Element>) en el mejor y más rápida manera posible, de manera que podemos utilizar ArrayList's operaciones en él, tales como get(index), add(element), etc.

Maksim
fuente

Respuestas:

360

Mejor use una biblioteca como Guava :

import com.google.common.collect.Lists;

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

Otro ejemplo de guayaba:

ImmutableList.copyOf(myIterator);

o Colecciones de Apache Commons :

import org.apache.commons.collections.IteratorUtils;

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

List<Element> myList = IteratorUtils.toList(myIterator);       
Renaud
fuente
8
No lo entiendo ¿De qué manera es la ArrayList devuelta por, digamos, Guava, mejor que una ArrayList normal? ¿Lo hacen de una manera más eficiente? Incluso si es más eficiente, ¿realmente vale la pena agregar una dependencia adicional (y más complejidad) a su proyecto?
CorayThan
10
@CorayThan menos código + métodos probados. Aunque estoy de acuerdo con usted, no agregaría una dependencia adicional solo para usar ese método. Pero, de nuevo, la mayoría de mis proyectos (grandes) usan Guava o Apache Commons ...
Renaud
44
@CorayThan Divide y conquista a mi amigo. ¿Por qué escribir un método que ya proporciona una biblioteca y se prueba? Estamos utilizando muchos Apache Commons y Guava, son increíbles y te ayudan a ahorrar tiempo y dinero.
Stephan
55
De acuerdo con Renaud y Stephan. Además, si está utilizando una herramienta de compilación, es lo más fácil del mundo incluir estas bibliotecas ... TAMBIÉN ... si realmente no desea incluirlas, puede ir a la fuente del código Apache / Guava y copia lo que han hecho allí: MUCHO mejor que perder el tiempo reinventando los cientos de ruedas bellamente diseñadas y probadas que ya están disponibles.
Mike rodent
238

En Java 8, puede usar el nuevo forEachRemainingmétodo que se ha agregado a la Iteratorinterfaz:

List<Element> list = new ArrayList<>();
iterator.forEachRemaining(list::add);
Stuart Marks
fuente
2
lo que significa ::? que nombre tiene parece una referencia directa a list.add (); y parece también algo nuevo de java8; ¡y gracias! :)
Aquarius Power
13
@AquariusPower La ::sintaxis es nueva en Java 8, y se refiere a una "referencia de método", que es una forma abreviada de lambda. Consulte aquí para obtener más información: docs.oracle.com/javase/tutorial/java/javaOO/…
Stuart Marks
de donde iteratorviene es un símbolo no resuelto
javadba
1
@javadba La pregunta original era sobre cómo convertir un iterador que ya tiene en una nueva ArrayList. Para los propósitos de este ejemplo, se supone que se llama al iterador que ya tiene iterator.
Stuart Marks el
1
Esta debería ser la respuesta aceptada, ya que es la más corta y no depende de bibliotecas de terceros
DanielCuadra
64

Puede copiar un iterador a una nueva lista como esta:

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

Eso supone que la lista contiene cadenas. Realmente no hay una manera más rápida de recrear una lista desde un iterador, estás atrapado en recorrerla a mano y copiar cada elemento en una nueva lista del tipo apropiado.

EDITAR:

Aquí hay un método genérico para copiar un iterador a una nueva lista de forma segura:

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

Úselo así:

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

Tenga en cuenta que hay una diferencia entre Iterabley Iterator.

Si tiene un Iterable, entonces con Java 8 puede usar esta solución:

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

Como sé Collectors.toList()crea ArrayListinstancia.

En realidad, en mi opinión, también se ve bien en una línea.
Por ejemplo, si necesita regresar List<Element>de algún método:

return StreamSupport.stream(iter.spliterator(), false).collect(Collectors.toList());
Dub Nazar
fuente
44
La pregunta es sobre Iterator <Element> como punto de partida, no Iterable <Element>.
Jaap
1
De acuerdo con el comentario anterior. súper confuso que hayas nombrado tu Iterable iterator. Son completamente diferentes.
Stephen Harrison
En Java 8 puede convertir fácilmente una Iteratorcopia de seguridad a un solo uso Iterable mediante el uso () -> iterator. Esto puede ser útil en situaciones como esta, pero permítanme enfatizar lo importante nuevamente: puede usar este método solo si un solo uso Iterable es aceptable. Llamar iterable.iterator()más de una vez dará resultados inesperados. La respuesta anterior se convierte entonces enIterator<Element> iterator = createIterator(); List<Element> array = StreamSupport.stream(((Iterable<Element>) () -> iterable).spliterator(), false).collect(toList());
neXus
9

Solución bastante concisa con Java 8 simple 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)
    );
}
xehpuk
fuente
¿Hay una forma más compacta de escribir esto usando la API de flujo? Parece no más simple que la forma normal de bucle while.
xi.lin
37
No llamaría a esa solución "concisa".
Sergio
1
@ Sergio Por eso escribí "bonita". Sin embargo, no necesita variables locales y solo un punto y coma. Puede acortarlo con importaciones estáticas.
xehpuk
1
Yo diría que iterator.forEachRemaining( list::add ), también nuevo en Java 8, es mucho más conciso. Insertar la variable de lista en Collectorno mejora la lectura en este caso, ya que debe ser compatible con a Streamy a Spliterator.
Sheepy
1
@ Sergio No es corto, pero es una sola expresión, lo que hace una gran diferencia.
michaelsnowden
5
List result = new ArrayList();
while (i.hasNext()){
    result.add(i.next());
}
Akvel
fuente
11
@LuggiMendoza: Stack Overflow no está destinado a ser un lugar en el que puedas cortar, pegar y resolver tu problema. Está destinado a ser informativo. Esta es una respuesta perfectamente informativa y cualquier persona razonable debería poder decir que yo era un iterador. Por lo que parece, casi no te esfuerzas en tratar de entender lo que estaba sucediendo.
CaTalyst.X
2

Probar StickyListde Cactoos :

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

Descargo de responsabilidad: soy uno de los desarrolladores.

yegor256
fuente
Esto no responde la pregunta. Iterable! =Iterator
xehpuk
Genial, ahora probablemente necesite generar JavaDoc para la nueva versión y actualizar el enlace. :)
xehpuk
2

Aquí hay una línea usando Streams

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

usa google guava !

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

++

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

Aquí, en este caso, si quieres la forma más rápida posible, entonces for loopes mejor.

El iterador sobre un tamaño de muestra de 10,000 runstomas 40 msdonde en cuanto a tomas de bucle2 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));  

Eso supone que la lista contiene cadenas.

vikiiii
fuente
3
Seguramente, usar un forbucle y acceder a los elementos de una lista get(i)es más rápido que usar un iterador ... pero eso no es lo que pedía el OP, mencionó específicamente que se proporciona un iterador como entrada.
Óscar López
@ Oscar lo siento, ¿debo eliminar mi respuesta?
vikiiii
Esa es tu llamada. Todavía no lo eliminaría, podría ser informativo. Solo borro mis respuestas cuando la gente comienza a desestimarlas :)
Óscar López
Creo que @ ÓscarLópez tiene razón ... esta puede no ser la respuesta requerida, pero contiene información útil para los lectores (como yo: P).
Proyecto Chthonic
Tienes un error en tu respuesta. En el get(int)bucle, solo está viendo 100k de las entradas de 1m. Si cambia ese ciclo para que sea it.size(), verá que estos 2 métodos están cerca de la misma velocidad. En general, este tipo de pruebas de velocidad de Java proporcionan información limitada sobre el rendimiento en la vida real y deben considerarse con escepticismo.
Gray