Rendimiento del bucle for vs Iterator / foreach tradicional en Java

¿Hay algún resultado de prueba de rendimiento disponible al comparar el bucle for vs Iterator tradicional al atravesar ArrayList, HashMap y otras colecciones?

O simplemente, ¿por qué debería usar Iterator en bucle o viceversa?

Asumiendo que esto es lo que querías decir:

 // traditional for loop for (int i = 0; i < collection.size(); i++) { T obj = collection.get(i); // snip } // using iterator Iterator iter = collection.iterator(); while (iter.hasNext()) { T obj = iter.next(); // snip } // using iterator internally (confirm it yourself using javap -c) for (T obj : collection) { // snip } 

Iterator es más rápido para colecciones sin acceso aleatorio (p. Ej., TreeSet, HashMap, LinkedList). Para arrays y ArrayLists, las diferencias de rendimiento deben ser insignificantes.

Editar: Creo que la micro-evaluación comparativa es la raíz de casi el mal, al igual que la optimización temprana. Pero, de nuevo, creo que es bueno sentir las implicaciones de cosas tan triviales. Por lo tanto, he realizado una pequeña prueba :

  • iterar sobre LinkedList y ArrayList respecivamente
  • con 100.000 cadenas “aleatorias”
  • resumiendo su longitud (algo para evitar que el comstackdor optimice todo el ciclo)
  • usando los 3 estilos de bucle (iterador, para cada uno, con contador)

Los resultados son similares para todos, pero “para con contador” con LinkedList. Los otros cinco tomaron menos de 20 milisegundos para iterar en toda la lista. Usar list.get(i) en una LinkedList 100,000 veces tomó más de 2 minutos (!) En completarse (60,000 veces más lento). ¡Guauu! 🙂 Por lo tanto, es mejor utilizar un iterador (que se utiliza explícita o implícitamente para cada uno), especialmente si no sabes de qué tipo y tamaño de lista se trata.

La primera razón para usar un iterador es una corrección obvia . Si utiliza un índice manual, puede haber errores muy inocuos que solo puede ver si observa con atención: ¿comenzó en 1 o en 0? ¿Terminaste al length - 1 ? ¿Usaste < o <= ? Si usa un iterador, es mucho más fácil ver que realmente está iterando todo el conjunto. "Di lo que haces, haz lo que dices".

La segunda razón es el acceso uniforme a diferentes estructuras de datos. Se puede acceder de manera eficiente a una matriz a través de un índice, pero una lista vinculada se recorre mejor recordando el último elemento al que se accedió (de lo contrario obtendrá un " Shlemiel, el pintor "). Un hashmap es aún más complicado. Al proporcionar una interfaz uniforme a partir de estas y otras estructuras de datos (por ejemplo, también puede realizar recorridos de árbol), obtiene una corrección obvia de nuevo. La lógica de desplazamiento debe implementarse solo una vez, y el código que la utiliza puede decir "de manera concisa lo que hace y hacer lo que dice".

El rendimiento es similar en la mayoría de los casos.

Sin embargo, cada vez que un código recibe una Lista y realiza un bucle, existe un caso conocido:
el iterador es mucho mejor para todas las implementaciones de List que no implementan RandomAccess (ejemplo: LinkedList).

La razón es que para estas listas, acceder a un elemento por índice no es una operación de tiempo constante.

Por lo tanto, también puede considerar el iterador como más robusto (a los detalles de implementación).


Como siempre, el rendimiento no debe ocultar los problemas de legibilidad.
El loop foreach java5 es un gran éxito en ese aspecto 🙂

Yo no creo eso

 for (T obj : collection) { 

calcula .size () cada vez que pasa el ciclo y, por lo tanto, es más rápido que

 for (int i = 0; i < collection.size(); i++) { 

Una de las mejores razones para usar un iterador sobre la syntax de i ++ es que no todas las estructuras de datos admitirán el acceso aleatorio y mucho menos tendrán un buen rendimiento. También debería progtwigr en la lista o en la interfaz de recostackción para que, si luego decidió que otra estructura de datos sería más eficiente, podría canjearla sin una cirugía masiva. En ese caso (el caso de la encoding de una interfaz), no necesariamente conocerá los detalles de la implementación y probablemente sea más prudente posponer eso a la estructura de datos en sí misma.

Una de las razones por las que aprendí a usar el para cada uno es que simplifica los bucles nesteds, especialmente en más de 2 bucles dimensionales. Todos los i’s, j’s yk’s que puedes terminar manipulando pueden ser confusos muy rápidamente.

Usa JAD o JD-GUI contra tu código generado, y verás que no hay diferencia real. La ventaja del nuevo formulario de iterador es que se ve más limpio en su base de código.

Editar : Veo por las otras respuestas que en realidad quiso decir la diferencia entre usar get (i) versus un iterador. Tomé la pregunta original para significar la diferencia entre las formas antiguas y las nuevas de usar el iterador.

Usar get (i) y mantener su propio contador, especialmente para las clases de la List , no es una buena idea, por los motivos mencionados en la respuesta aceptada.

Sí, marca la diferencia en las colecciones que no son de acceso aleatorio como LinkedList. Una lista vinculada internamente se implementa mediante nodos que apuntan a la siguiente (comenzando en un nodo principal).

El método get (i) en una lista vinculada comienza desde el nodo principal y navega a través de los enlaces hasta el i-ésimo nodo. Cuando itera en la lista vinculada utilizando un bucle for tradicional, comienza de nuevo desde el nodo principal cada vez, por lo tanto, el recorrido global se convierte en tiempo cuadrático.

 for( int i = 0; i< list.size(); i++ ) { list.get(i); //this starts everytime from the head node instead of previous node } 

Mientras que para cada ciclo itera sobre el iterador obtenido de la lista vinculada y llama a su método next (). El iterador mantiene los estados del último acceso y por lo tanto no comienza desde la cabeza en todo momento.

 for( Object item: list ) { //item element is obtained from the iterator's next method. } 

+1 a lo que dijo sfussenegger. Para su información, si utiliza un iterador explícito o uno implícito (es decir, para cada uno) no hará una diferencia en el rendimiento porque comstackn con el mismo código de bytes.

    Intereting Posts