2014-05-04 22 views
6

C'è qualche differenza tra le prestazioni tra i due frammenti di codice qui sotto?C'è qualche differenza tra questi due anelli?

for(String project : auth.getProjects()) { 
    // Do something with 'project' 
} 

e

String[] projects = auth.getProjects(); 
for(String project : projects) { 
    // Do something with 'project' 
} 

Per quanto mi riguarda, penso che il secondo è meglio, ma è più lungo. Il primo è più breve, ma non sono sicuro se sia più veloce. Non sono sicuro, ma a me sembra che ogni volta che il ciclo viene iterato, viene chiamato auth.getProjects. Non è così?

+0

Si chiama solo una volta, ma la seconda versione è troppo prolissa. Tutto quello che stai facendo è passare attraverso una lista. Una riga è sufficiente. – Navin

risposta

10

Modifica: @StephenC ha ragione, il JLS è un posto molto migliore per trovare una risposta per qualcosa di questa natura. Ecco uno link to the enhanced for loop nelle specifiche della lingua. Lì scoprirai che ci sono diversi tipi di istruzioni che genera, ma nessuno di loro chiamerebbe il metodo più di una volta.


Semplice test dimostra che il metodo viene chiamato una sola volta

public class TestA { 
    public String [] theStrings; 

    public TestA() { 
     theStrings = new String[] {"one","two", "three"}; 
     for(String string : getTheStrings()) { 
      System.out.println(string); 
     } 
    } 

    public String[] getTheStrings() { 
     System.out.println("get the strings"); 
     return theStrings; 
    } 

    public static void main(String [] args) { 
     new TestA(); 
    } 
} 

uscita:

get the strings 
one 
two 
three 

Quindi, in sostanza essi sono la stessa cosa. L'unica cosa che potrebbe essere utile per il secondo sarebbe se si desidera utilizzare l'array al di fuori del ciclo for.


Modifica

You got me curioso di sapere come il compilatore Java gestito questo modo utilizzando il codice di cui sopra ho decompilato il file di classe e heres quello che il risultato è

public class TestA 
{ 

    public TestA() 
    { 
     String as[]; 
     int j = (as = getTheStrings()).length; 
     for(int i = 0; i < j; i++) 
     { 
      String string = as[i]; 
      System.out.println(string); 
     } 

    } 

    public String[] getTheStrings() 
    { 
     System.out.println("get the strings"); 
     return theStrings; 
    } 

    public static void main(String args[]) 
    { 
     new TestA(); 
    } 

    public String theStrings[] = { 
     "one", "two", "three" 
    }; 
} 

come si può vedi il compilatore semplicemente ristrutturato il tuo ciclo for in un loop standard! Dimostra inoltre che, in effetti, sono esattamente gli stessi dopo che il compilatore è riuscito a farcela.

+0

Grazie. Ora ho un'idea chiara di cosa succede dietro le quinte. – hamid

+3

* "Il test semplice mostra che il metodo viene chiamato solo una volta" * - I test semplici possono essere fuorvianti ... 'perché non sai cosa dovresti testare. Un approccio migliore è leggere il JLS. –

+0

+1 per la tua bella spiegazione –

-1

Il secondo è un miglioramento delle prestazioni, non crea una variabile più volte. Pertanto, sì, auth.getProjects viene chiamato ogni volta.

+0

errato. La variabile locale per contenere 'auth.getProjects()' viene creata e assegnata solo una volta. Vedi la mia risposta. –

+0

Mi dispiace, l'ho scritto di notte xD – loafy

3

Ci sono alcune altre operazioni nel secondo esempio. Invece di fare riferimento alla matrice usando direttamente il metodo, si dichiara una nuova variabile di riferimento, la si memorizza in quella nuova variabile, quindi si fa riferimento alla nuova variabile.

È possibile estrarre il bytecode con ASM Bytecode Outline.

MA, questa è la micro-ottimizzazione. Se hai bisogno di una nuova variabile di riferimento, creane una. Se non ne hai bisogno, salva il lavoro e non crearne uno nuovo.

+1

Intendevi "secondo esempio" invece di "primo esempio"? Il secondo esempio è quello che dichiara una nuova variabile di riferimento. Sebbene, se la variabile di riferimento non viene utilizzata altrove nella funzione, la maggior parte dei compilatori probabilmente utilizzeranno un registro per questo in entrambi i casi. –

+1

@WarrenDew Sì, ho modificato la mia risposta, grazie per averlo indicato –

3

Assumendo che per in si intende :, non c'è alcuna differenza nelle prestazioni; entrambi fanno la stessa cosa Anche nel primo esempio, auth.getProjects() viene eseguito solo una volta; non può essere eseguito più volte, poiché se lo fosse, l'iterazione for dovrebbe ricominciare ogni volta, il che non è il modo in cui funziona.

Si consiglia di utilizzare la versione che si trova per essere più chiara.

-1

Entrambi sono uguali a quelli trovati sopra.

Ma una cosa è utilizzare 1st approcah e dopo il ciclo for, de-reference della variabile di progetto as- projects = null; Altrimenti, rimarrà in vita per tutta la durata del metodo e consumerà memoria inutile.

+0

errata. Il metodo getProjects non è chiamato "in the loop". Si chiama una volta, all'inizio del ciclo. –

+0

Modificato in base alla risposta corretta. – 53by97

2

Non c'è differenza.

Secondo la JLS 14.14.2, una maggiore for loop (per un tipo array) è equivalente ad una normale for ciclo con il seguente schema:

T[] #a = Expression; 
L1: L2: ... Lm: 
for (int #i = 0; #i < #a.length; #i++) { 
    {VariableModifier} TargetType Identifier = #a[#i]; 
    Statement 
} 

Se sostituiamo per il primo esempio:

for(String project : auth.getProjects()) { 
    // Do something with 'project' 
} 

otteniamo:

String[] $a = auth.getProjects(); 
for (int $i = 0; $i < $a.length; $i++) { 
    String project = $a[$i]; 
    // Do something with 'project' 
} 

Per il secondo esempio:

String[] projects = auth.getProjects(); 
for(String project : projects) { 
    // Do something with 'project' 
} 

otteniamo:

String[] projects = auth.getProjects(); 
String[] $a = projects; 
for (int $i = 0; $i < $a.length; $i++) { 
    String project = $a[$i]; 
    // Do something with 'project' 
} 

Le due versioni del codice sono chiaramente equivalenti, e assumendo che javac o JIT è in grado di ottimizzare rimuove la ridondanza variabile locale $a , le due versioni dovrebbero eseguire lo stesso.

Si noti che la variabile locale $a viene creata solo una volta all'inizio del ciclo in entrambi i casi.

Problemi correlati