2009-06-17 9 views
24

In Java, è più veloce per scorrere una matrice la vecchia maniera,modo più veloce per iterare un array in Java: variabile del ciclo vs migliorato per dichiarazione

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

oppure utilizzando il modulo più concisa,

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

Per una lista di array, la risposta è la stessa?

Naturalmente per la maggior parte del codice dell'applicazione, la risposta è che non fa alcuna differenza visibile quindi è necessario utilizzare la forma più concisa per la leggibilità. Tuttavia il contesto che sto osservando è un calcolo tecnico pesante, con operazioni che devono essere eseguite miliardi di volte, quindi anche una piccola differenza di velocità potrebbe finire per essere significativa.

+0

Perché non eseguire un punto di riferimento? Molto più accurato di una discussione. – Deestan

+0

È improbabile che notiate la differenza a meno che 'f()' sia estremamente breve. – EJP

+0

Questo è stato [chiesto prima] (http://stackoverflow.com/questions/256859/is-there-a-performance-difference-between-a-for-loop-and-a-for-each-loop). – RichardOD

risposta

53

Se si esegue il looping di un array, non dovrebbe avere importanza: il ciclo avanzato utilizza comunque gli accessi agli array.

Ad esempio, si consideri questo codice:

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

Quando decompilato con javap -c Test otteniamo (per il metodo 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 

Ora cambiarlo utilizzare un accesso esplicito array:

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

Questo si decompila 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 

C'è un po 'più di codice di configurazione nel ciclo avanzato per, ma fondamentalmente stanno facendo la stessa cosa. Nessun iteratore è coinvolto. Inoltre, mi aspetterei che ricevessero a JITted un codice ancora più simile.

Suggerimento: se davvero pensa che potrebbe fare una differenza significativa (che sarebbe solo mai fare se il corpo del ciclo è assolutamente minuscolo), allora si dovrebbe punto di riferimento con il vostro applicazione reale. Questa è l'unica situazione che conta.

21

Questo cade esattamente nell'arena di micro-optimization. Non importa davvero. Stilisticamente preferisco sempre la seconda perché è più concisa, a meno che non sia necessario il contatore dei loop per qualcos'altro. E questo è molto più importante di questo tipo di micro-ottimizzazione: leggibilità.

Detto questo, per un ArrayList non ci sarà molta differenza ma una LinkList sarà molto più efficiente con il secondo.

+4

Non utilizzerà un iteratore per un array. –

+1

il primo stile è limitato al numero intero di iterazioni Integer.MAX, mentre il secondo non lo è. – Chii

+2

Anche il secondo è limitato in questo modo perché gli array hanno un numero massimo di elementi Integer.MAX_VALUE e un ArrayList è supportato da un array. – cletus

6

Misuralo. La risposta a tutte le domande relative alle prestazioni può dipendere dalla versione VM, dal processore, dalla velocità di memoria, dalle cache, ecc. Quindi devi misurarlo per la tua particolare piattaforma.

Personalmente preferirei la seconda variante, perché l'intenzione è più chiara. Se le prestazioni diventano un problema, posso ottimizzarle in seguito, se questo codice è davvero importante per le prestazioni dell'intera applicazione.

1

su un array, o RandomAccess collezione è possibile ottenere un piccolo aumento della velocità facendo:

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

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

Ma io non mi preoccuperei in generale. Le ottimizzazioni come questa non comporteranno più del 0,1% di differenza sul tuo codice. Prova a invocare java con -prof per vedere dove il tuo codice sta effettivamente spendendo il suo tempo.

0

Ancora più veloce è utilizzare ParallelArray del framework fork-join (se si dispone di un set di dati sufficientemente grande).

Problemi correlati