2016-05-16 18 views
5

considerare quanto segue:Si tratta di un'ottimizzazione utile per i loop in java?

1.

for (final Bar a : bars) { 
     for (final Foo f : foos) { 
      doSomethingWith(f.foo(), a.bar()); 
     } 
    } 

e:

2.

for (final Bar a : bars) { 
     final Object bar = a.bar(); 
     for (final Foo f : foos) { 
      doSomethingWith(f.foo(), bar); 
     } 
    } 

è questo tipo di ottimizzazione davvero disponibile o sarà il compilatore farà automaticamente in ogni caso?

La risposta cambierà se bar() è un getter? (ad esempio, getBar())

La tua risposta cambierà se il mio obiettivo è lo sviluppo Android?

+2

di lavoro per lo studente: compilare entrambe le versioni e confrontare l'output bytecode. Ecco un riferimento; https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javap.html – DwB

+0

Beh, non penso che questa domanda sia una domanda a livello di studenti ma vabbè mi stavo chiedendo se qualcuno già conosce la risposta a questo invece di dirmi che è possibile guardare il bytecode di output, che so che potrei. e anche, dovrei guardare le uscite per Android. questo può essere un diavolo di un progetto per rispondere a questa domanda da solo. –

+1

Il compilatore può sapere che 'a.bar()' restituirà sempre lo stesso valore per un dato 'a'? –

risposta

2

Ho provato due esempi come da te per la tua domanda. Su questa base devo dire che il secondo approccio sarebbe meglio. (Anche se io non sto considerando Multi-Threading)

Test.java

public class Test{ 

    public static void main(String... args){ 

     String[][] arr2 = new String[5][5]; 
     for (final String[] obj : arr2) 
     { 
      for (final String str : obj) 
      System.out.println(str.length() +" " + obj.length); 
     } 
    } 
} 

dopo la compilazione e poi di nuovo la decompilazione ho ottenuto questo.

* Decompiled with CFR 0_114. 
*/ 
import java.io.PrintStream; 

public class Test { 
    public static /* varargs */ void main(String ... arrstring) { 
     String[][] arrstring2; 
     String[][] arrstring3 = arrstring2 = new String[5][5]; 
     int n = arrstring3.length; 
     for (int i = 0; i < n; ++i) { 
      String[] arrstring4; 
      for (String string : arrstring4 = arrstring3[i]) { //assignment will take place m*n. 
       System.out.println("" + string.length() + " " + arrstring4.length); 
      //this arrstring4.length will execute m*n (in this case).So, this will less efficient than others. 
      } 
     } 
    } 
} 

Test1.java

public class Test1{ 

    public static void main(String... args){ 

     String[][] arr2 = new String[5][5]; 
     for (final String[] obj : arr2) 
     { 
      int value = obj.length; 
      for (final String str : obj) 
       System.out.println(str.length() +" " + value); 
     } 
    } 
} 

dopo la compilazione e poi di nuovo la decompilazione ho ottenuto questo.

/* 
* Decompiled with CFR 0_114. 
*/ 
import java.io.PrintStream; 

public class Test1 { 
    public static /* varargs */ void main(String ... arrstring) { 
     String[][] arrstring2; 
     for (String[] arrstring3 : arrstring2 = new String[5][5]) { 
      int n = arrstring3.length; //Assignment will take place M times only. 
      //this will calculate M times only. So, it will definitely faster than above. 
      for (String string : arrstring3) { 
       System.out.println("" + string.length() + " " + n); 
       //here n is calculate M times but can be printed M*N times. 
      } 
     } 
    } 
} 
-2

Dopo aver visto i commenti e di dover rispondere su di loro, mi sono reso conto che anche se il compilatore avrebbe tuffarsi in comprensione a.bar() 's codice, sarebbe impossibile Garantiamo che a.bar() risultato non cambierebbe in un multi-threaded ambiente come java, anche, in congiunzione con Reflection se bar è un campo membro che può cambiare attraverso riflessioni e il compilatore non saprebbe che in anticipo può causare un semplice return bar; essere imprevedibile.

Così attualmente, mi sembra razionale continuare a preoccuparmi di utilizzare quell'ottimizzazione nel mio codice.

+0

"garantisce che il risultato' a.bar() non cambierà in un ambiente multi-thread "- a meno che il metodo non contenga qualche sincronizzazione o amici (come' volatile'), il compilatore è libero di ignorare altri thread. Senza la sincronizzazione, il JMM non garantisce alcuna visibilità delle modifiche apportate da altri thread e né l'HW garantisce nulla (si pensi alle cache core). +++ Detto questo, potrebbe essere un'ottimizzazione utile su Android, che non è così intelligente come la JVM della classe server. – maaartinus

+0

Si noti che la modifica del membro potrebbe verificarsi tra iterazioni su oggetti foos, volatile non sarà di aiuto in questo caso e la sincronizzazione potrebbe essere d'aiuto solo se eseguita correttamente. trovo difficile credere che un compilatore possa analizzare la correttezza della sincronizzazione in tutti i casi ... –

+0

Cosa succede se 'a.bar()' è un metodo 'final' che restituisce un campo' final'? – shmosel

0

sorprendente come sia sembra provare:

for (final Bar a : bars) { 
    innerLoop(a, foos); 
} 

private final innerLoop(Bar a, Collection<Foo> foos) { 
    for (final Foo f : foos) { 
     doSomethingWith(f.foo(), a.bar()); 
} 

solo un piccolo ritocco, fare un tentativo

+0

Cosa speri di ottenere da questo? Btw. hai anche un errore di sintassi. – Hulk