¿Cuál es, si la hay, la diferencia de rendimiento entre los siguientes dos bucles?
for (Object o: objectArrayList) {
o.DoSomething();
}
y
for (int i=0; i<objectArrayList.size(); i++) {
objectArrayList.get(i).DoSomething();
}
java
performance
for-loop
eflles
fuente
fuente
Respuestas:
Del artículo 46 en Java efectivo por Joshua Bloch:
fuente
Todos estos bucles hacen exactamente lo mismo, solo quiero mostrarlos antes de tirar mis dos centavos.
Primero, la forma clásica de recorrer List:
En segundo lugar, la forma preferida, ya que es menos propenso a errores (¿cuántas veces USTEDES ha hecho "oops, mezclado las variables i y j en estos bucles dentro de los bucles"?).
En tercer lugar, el micro optimizado para el bucle:
Ahora los dos centavos reales: al menos cuando estaba probando estos, el tercero fue el más rápido al contar milisegundos sobre cuánto tiempo tomó para cada tipo de bucle con una operación simple que se repitió unos pocos millones de veces: esto estaba usando Java 5 con jre1.6u10 en Windows en caso de que alguien esté interesado.
Si bien al menos parece ser que el tercero es el más rápido, realmente debería preguntarse si quiere correr el riesgo de implementar esta optimización de mirilla en todas partes en su código de bucle, ya que, por lo que he visto, el bucle real no es Por lo general, es la parte más lenta de cualquier programa real (o tal vez solo estoy trabajando en el campo equivocado, quién sabe). Y también, como mencioné en el pretexto para el bucle for-each de Java (algunos se refieren a él como bucle Iterator y otros como bucle for-in ), es menos probable que golpee ese error estúpido en particular al usarlo. Y antes de debatir cómo esto incluso puede ser incluso más rápido que los otros, recuerde que javac no optimiza en absoluto el código de bytes (bueno, casi de todos modos), solo lo compila.
Sin embargo, si está interesado en la microoptimización y / o su software utiliza muchos bucles recursivos, puede estar interesado en el tercer tipo de bucle. Solo recuerde comparar su software bien antes y después de cambiar los bucles for que tiene para este extraño y micro optimizado.
fuente
get(int)
, el otro usa unIterator
. ConsidereLinkedList
dónde el rendimiento defor(int i=0;i<strings.size();i++) { /* do something using strings.get(i) */ }
es mucho peor, ya que lo está haciendoget(int)
n veces.El ciclo for-each generalmente debería preferirse. El enfoque "get" puede ser más lento si la implementación de la Lista que está utilizando no admite acceso aleatorio. Por ejemplo, si se usa una LinkedList, incurriría en un costo transversal, mientras que el enfoque para cada usa un iterador que realiza un seguimiento de su posición en la lista. Más información sobre los matices del ciclo for-each .
Creo que el artículo ya está aquí: nueva ubicación
El enlace que se muestra aquí estaba muerto.
fuente
Bueno, el impacto en el rendimiento es en su mayoría insignificante, pero no es cero. Si nos fijamos en JavaDoc de la
RandomAccess
interfaz:Y for-each loop está usando la versión con iterador,
ArrayList
por ejemplo, for-each loop no es más rápido.fuente
Iterable
s, pero no matrices. Para las matrices se usa un bucle for con una variable de índice. Hay información sobre esto en el JLS .Parece haber una diferencia desafortunadamente.
Si observa el código de bytes generado para ambos tipos de bucles, son diferentes.
Aquí hay un ejemplo del código fuente Log4j.
En /log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java tenemos una clase interna estática llamada Log4jMarker que define:
Con bucle estándar:
Con para cada uno:
¿Qué pasa con ESO Oracle?
He intentado esto con Java 7 y 8 en Windows 7.
fuente
Siempre es mejor usar el iterador en lugar de indexar. Esto se debe a que el iterador probablemente esté optimizado para la implementación de la Lista mientras que indexado (llamando a get) podría no estarlo. Por ejemplo, LinkedList es una Lista, pero la indexación a través de sus elementos será más lenta que iterar usando el iterador.
fuente
foreach aclara la intención de su código y eso normalmente se prefiere a una mejora de velocidad muy pequeña, si la hay.
Cada vez que veo un bucle indexado, tengo que analizarlo un poco más para asegurarme de que hace lo que creo que hace, por ejemplo, ¿Comienza desde cero, incluye o excluye el punto final, etc.?
Parece que la mayor parte del tiempo la paso leyendo el código (que escribí o alguien más escribió) y la claridad es casi siempre más importante que el rendimiento. Es fácil descartar el rendimiento en estos días porque Hotspot hace un trabajo increíble.
fuente
El siguiente código:
Da el siguiente resultado en mi sistema:
Estoy ejecutando Ubuntu 12.10 alpha con OracleJDK 1.7 actualización 6.
En general, HotSpot optimiza muchas indirecciones y simples operaciones redundantes, por lo que en general no debe preocuparse por ellas a menos que haya muchas de ellas en secuencia o estén muy anidadas.
Por otro lado, la entrada indexada en LinkedList es mucho más lenta que llamar al siguiente en el iterador para LinkedList, por lo que puede evitar ese impacto en el rendimiento mientras mantiene la legibilidad cuando usa iteradores (explícita o implícitamente en cada ciclo).
fuente
Incluso con algo como un ArrayList o un Vector, donde "get" es una simple búsqueda de matriz, el segundo bucle todavía tiene una sobrecarga adicional que el primero no. Esperaría que fuera un poco más lento que el primero.
fuente
La única forma de saberlo con certeza es compararlo, e incluso eso no es tan simple como puede parecer . El compilador JIT puede hacer cosas muy inesperadas a su código.
fuente
Aquí hay un breve análisis de la diferencia presentada por el equipo de desarrollo de Android:
https://www.youtube.com/watch?v=MZOf3pOAM6A
El resultado es que no es una diferencia, y en entornos muy restringidos con listas muy grandes podría ser una diferencia notable. En sus pruebas, el tiempo de cada ciclo tomó el doble. Sin embargo, su prueba se realizó sobre una lista de 400,000 enteros. La diferencia real por elemento en la matriz fue de 6 microsegundos . No lo he probado y no dijeron, pero esperaría que la diferencia sea un poco más grande usando objetos en lugar de primitivos, pero aún así, a menos que esté construyendo código de biblioteca donde no tenga idea de la escala de lo que se le preguntará para repetir, creo que no vale la pena destacar la diferencia.
fuente
Por el nombre de la variable
objectArrayList
, supongo que es una instancia dejava.util.ArrayList
. En ese caso, la diferencia de rendimiento sería imperceptible.Por otro lado, si se trata de una instancia de
java.util.LinkedList
, el segundo enfoque será mucho más lento como elList#get(int)
es una operación O (n).Por lo tanto, siempre se prefiere el primer enfoque a menos que la lógica en el ciclo necesite el índice.
fuente
Ambos hacen lo mismo, pero para un uso de programación fácil y seguro para cada uno, hay posibilidades de errores propensos en la segunda forma de uso.
fuente
Es extraño que nadie haya mencionado lo obvio: foreach asigna memoria (en forma de iterador), mientras que un bucle normal no asigna ninguna memoria. Para los juegos en Android, esto es un problema, porque significa que el recolector de basura se ejecutará periódicamente. En un juego no quieres que el recolector de basura se ejecute ... NUNCA. Por lo tanto, no use bucles foreach en su método de dibujo (o render).
fuente
La respuesta aceptada responde a la pregunta, aparte del caso excepcional de ArrayList ...
Como la mayoría de los desarrolladores confían en ArrayList (al menos eso creo)
Así que estoy obligado a agregar la respuesta correcta aquí.
Directamente de la documentación del desarrollador: -
El bucle for mejorado (también conocido a veces como bucle "for-each") se puede usar para colecciones que implementan la interfaz Iterable y para matrices. Con las colecciones, se asigna un iterador para realizar llamadas de interfaz a hasNext () y next (). Con una ArrayList, un bucle contado escrito a mano es aproximadamente 3 veces más rápido (con o sin JIT), pero para otras colecciones, la sintaxis mejorada para el bucle será exactamente equivalente al uso explícito del iterador.
Hay varias alternativas para iterar a través de una matriz:
zero () es el más lento, porque el JIT aún no puede optimizar el costo de obtener la longitud de la matriz una vez por cada iteración a través del ciclo.
uno () es más rápido. Saca todo a las variables locales, evitando las búsquedas. Solo la longitud de la matriz ofrece un beneficio de rendimiento.
two () es más rápido para dispositivos sin un JIT e indistinguible de one () para dispositivos con un JIT. Utiliza la sintaxis mejorada para bucles introducida en la versión 1.5 del lenguaje de programación Java.
Por lo tanto, debe usar el bucle for mejorado de forma predeterminada, pero considere un bucle contado escrito a mano para la iteración ArrayList crítica para el rendimiento.
fuente
Sí, la
for-each
variante es más rápida de lo normal.index-based-for-loop
.for-each
usos variantesiterator
. Por lo tanto, el desplazamiento es más rápido que elfor
bucle normal, que se basa en índices.Esto se debe a que
iterator
están optimizados para atravesar, porque apunta justo antes del siguiente elemento y justo después del elemento anterior . Una de las razones paraindex-based-for-loop
ser lento es que tiene que calcular y moverse a la posición del elemento cada vez que no está con eliterator
.fuente
fuente