2009-06-17 107 views
24

En Java, es más rápido para recorrer una matriz a la antigua usanza,La forma más rápida para recorrer un array en Java: variable de bucle vs mejorado para la declaración

for (int i = 0; i < a.length; i++) 
    f(a[i]); 

o utilizando el formulario de más concisa,

for (Foo foo : a) 
    f(foo); 

Para una ArrayList, ¿la respuesta es la misma?

Por supuesto, para la gran mayoría del código de la aplicación, la respuesta es que no hace una diferencia perceptible por lo que la forma más concisa se debe utilizar para la legibilidad. Sin embargo, el contexto que estoy viendo es el cálculo técnico de trabajo pesado, con operaciones que deben realizarse miles de millones de veces, por lo que incluso una pequeña diferencia de velocidad podría llegar a ser significativa.

+0

Por qué no corren un punto de referencia? Mucho más preciso que una discusión. – Deestan

+0

Es poco probable que note la diferencia a menos que 'f()' sea extremadamente corto. – EJP

+0

Esto ha sido [preguntado antes] (http://stackoverflow.com/questions/256859/is-there-a-performance-difference-between-a-for-loop-and-a-for-each-loop). – RichardOD

Respuesta

53

Si está ejecutando un bucle en una matriz, no debería importar: de todos modos, el bucle forzado mejorado usa acceso a la matriz.

Por ejemplo, considere este código:

public static void main(String[] args) 
{ 
    for (String x : args) 
    { 
     System.out.println(x); 
    } 
} 

Cuando descompilada con javap -c Test obtenemos (para el método main):

public static void main(java.lang.String[]); 
    Code: 
    0: aload_0 
    1: astore_1 
    2: aload_1 
    3: arraylength 
    4: istore_2 
    5: iconst_0 
    6: istore_3 
    7: iload_3 
    8: iload_2 
    9: if_icmpge 31 
    12: aload_1 
    13: iload_3 
    14: aaload 
    15: astore 4 
    17: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    20: aload 4 
    22: invokevirtual #3; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    25: iinc 3, 1 
    28: goto 7 
    31: return 

Ahora cambiarlo a utilizar un explícita de acceso a conjunto:

public static void main(String[] args) 
{ 
    for (int i = 0; i < args.length; i++) 
    { 
     System.out.println(args[i]); 
    } 
} 

Esto se descompila a:

public static void main(java.lang.String[]); 
    Code: 
    0: iconst_0 
    1: istore_1 
    2: iload_1 
    3: aload_0 
    4: arraylength 
    5: if_icmpge 23 
    8: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    11: aload_0 
    12: iload_1 
    13: aaload 
    14: invokevirtual #3; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    17: iinc 1, 1 
    20: goto 2 
    23: return 

Hay un poco más de código de configuración en el bucle forzado mejorado, pero básicamente están haciendo lo mismo. No hay iteradores involucrados. Además, esperaría que se JITted a un código aún más similar.

Sugerencia: si realmente piensa que podría hacer una diferencia significativa (que solo alguna vez hacer si el cuerpo del bucle es absolutamente minúsculo), entonces debe compararlo con su aplicación real. Esa es la única situación que importa.

21

Esto cae directamente en la arena de micro-optimization. Realmente no importa. Estilísticamente, siempre prefiero el segundo porque es más conciso, a menos que necesites el contador de bucle para otra cosa. Y eso es mucho más importante que este tipo de micro-optimización: legibilidad.

Dicho esto, para una ArrayList no habrá mucha diferencia, pero una LinkedList será mucho más eficiente con la segunda.

+4

No usará un iterador para una matriz. –

+1

el primer estilo está limitado a Integer.MAX número de iteraciones, mientras que el segundo no. – Chii

+2

El segundo también está limitado de esa manera porque las matrices tienen un número máximo de elementos Integer.MAX_VALUE y ArrayList está respaldado por una matriz. – cletus

6

Mídalo. La respuesta a todas las preguntas de rendimiento puede depender de la versión de VM, el procesador, la velocidad de memoria, las memorias caché, etc. Por lo tanto, debe medirla para su plataforma en particular.

Personalmente preferiría la segunda variante, porque la intención es más clara. Si el rendimiento se convierte en un problema, puedo optimizarlo más tarde de todos modos, si ese código es realmente importante para el rendimiento de toda la aplicación.

1

En una matriz o colección de acceso aleatorio se puede obtener un pequeño incremento en la velocidad haciendo:

List<Object> list = new ArrayList<Object>(); 

for (int i=0, d=list.size(); i<d; i++) { 
    something(list.get(i)); 
} 

pero no me preocupe en general. Las optimizaciones como esta no harán una diferencia de más del 0.1% en su código. Intente invocar java con -prof para ver dónde está gastando realmente su código.

0

Aún más rápido es usar el ParallelArray de la estructura fork-join (si tiene un conjunto de datos suficientemente grande).

Cuestiones relacionadas