Java: la mejor manera de iterar a través de una colección (aquí ArrayList)

98

Hoy estaba felizmente codificando cuando llegué a un fragmento de código que ya usé cientos de veces:

Iterando a través de una colección (aquí ArrayList)

Por alguna razón, miré las opciones de autocompletado de Eclipse y me pregunto:

¿En qué casos es mejor utilizar los siguientes bucles que los demás?

El ciclo de índice de matriz clásico:

for (int i = 0; i < collection.length; i++) {
  type array_element = collection.get(index);
}

El iterador tieneNext () / next ():

for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
  type type = (type) iterator.next();   
}

Y mi favorito porque es muy sencillo de escribir:

for (iterable_type iterable_element : collection) {

}
Jason Rogers
fuente
2
Cuando se trata de mí, uso principalmente el tercer ciclo.
Harry Joy
1
Para la segunda manera es mejor usar:for (Iterator<type> iterator = collection.iterator(); iterator.hasNext();) { type type = iterator.next(); }
Mike Jones
4
La interfaz de colección no contiene un método "get", por lo que el primero no siempre es posible.
ThePerson

Respuestas:

104

El primero es útil cuando también necesita el índice del elemento. Esto es básicamente equivalente a las otras dos variantes de ArrayLists, pero será muy lento si usa a LinkedList.

El segundo es útil cuando no necesita el índice del elemento, pero es posible que deba eliminar los elementos a medida que itera. Pero esto tiene la desventaja de ser un poco demasiado detallado en mi opinión.

La tercera versión también es mi opción preferida. Es breve y funciona para todos los casos en los que no necesita ningún índice o el iterador subyacente (es decir, solo está accediendo a elementos, no eliminándolos ni modificando Collectionde ninguna manera, que es el caso más común).

MAK
fuente
3
+1 Gáname. iba a mencionar LinkedList (no todos Collection son baratos para recuperar por índice).
The Scrum Meister
Siempre que se requiera solo repetir elementos, siempre es bueno usar la tercera versión, porque la implementación ya existe para todas las diferentes Colecciones y Sun o cualquier implementación de JDK se esfuerza por mejorar el rendimiento en lugar de cada una.
Phani
@Phani: Las otras dos variantes también funcionan en cualquier implementación de JDK.
MAK
No dije que no funcionarían, pero si por casualidad se realizan ciertas mejoras o cambios de rendimiento para las implementaciones de colecciones, entonces se aplicaría automáticamente a su código y no necesita escribir para mejorar el rendimiento. es lo que quiero decir.
Phani
1
@Phani: AFAIK, la tercera forma es simplemente azúcar sintáctica para la segunda forma (es decir, bajo el capó, la variante foreach realmente usa un iterador). Por lo tanto, cualquier beneficio de rendimiento debería estar disponible para ambas variantes. La primera versión, por supuesto, no obtendría ningún beneficio de rendimiento (por ejemplo, si Listse implementa como un árbol, en realidad sería más lento).
MAK
36

Todos ellos tienen sus propios usos:

  1. Si tiene un iterable y necesita atravesar incondicionalmente a todos ellos:

    para (iterable_type iterable_element: colección)

  2. Si tiene un iterable pero necesita atravesar condicionalmente:

    para (Iterador iterador = colección.iterador (); iterador.hasNext ();)

  3. Si la estructura de datos no se implementa iterable:

    para (int i = 0; i <collection.length; i ++)

Suraj Chandran
fuente
¿Puede hacer esto para recorrer el primer método condicionalmente: if (iterable_element.some_attribute) {// hacer algo; } else {// no hacer nada; }. Si es así, entonces esencialmente no hay diferencia entre el primer y el segundo método, aparte del azúcar sintáctico.
Thomas Nguyen
1 es tan capaz de atravesar condicionalmente como los demás usando breaky / ocontinue
arcyqwerty
13

También hay una utilidad stream () de colecciones con Java 8

collection.forEach((temp) -> {
            System.out.println(temp);
});

o

collection.forEach(System.out::println);

Más información sobre el enlace y colecciones de Java 8 para maravillas

snr
fuente
4

Ninguno de ellos es "mejor" que los demás. El tercero es, para mí, más legible, pero para alguien que no usa foreaches puede parecer extraño (quizás prefieran el primero). Los 3 son bastante claros para cualquiera que entienda Java, así que elija el que le haga sentirse mejor con el código.

El primero es el más básico, por lo que es el patrón más universal (funciona para matrices, todos los iterables en los que puedo pensar). Esa es la única diferencia en la que puedo pensar. En casos más complicados (por ejemplo, necesita tener acceso al índice actual o necesita filtrar la lista), el primer y segundo caso pueden tener más sentido, respectivamente. Para el caso simple (objeto iterable, sin requisitos especiales), el tercero parece el más limpio.

Rafe Kettler
fuente
2

La primera opción es un mejor rendimiento (como ArrayList implementa la interfaz RandomAccess). Según el documento de Java, una implementación de List debería implementar la interfaz RandomAccess si, para instancias típicas de la clase, este bucle:

 for (int i=0, n=list.size(); i < n; i++)
     list.get(i);

corre más rápido que este bucle:

 for (Iterator i=list.iterator(); i.hasNext(); )
     i.next();

Espero que ayude. La primera opción sería lenta para listas de acceso secuenciales.

Amitav Padhi
fuente
1

Aquí hay un ejemplo

Query query = em.createQuery("from Student");
             java.util.List list = query.getResultList();
             for (int i = 0; i < list.size(); i++) 
             {

                 student = (Student) list.get(i);
                 System.out.println(student.id  + "  " + student.age + " " + student.name + " " + student.prenom);

             }
Abdelouhab
fuente