2016-05-16 5 views
5

ist folgendes zu beachten:Ist das eine hilfreiche Optimierung für Loops in Java?

1.

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

und:

2.

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

Ist diese Art der Optimierung wirklich hilfreich oder wird der Compiler tun es trotzdem automatisch?

Ändert sich Ihre Antwort, wenn bar() ein Getter ist? (z. B. getBar())

Wird sich Ihre Antwort ändern, wenn ich Android-Entwicklung anstrebe?

+2

Arbeit für die Schüler: beide Versionen kompilieren und den Bytecode Ausgabe vergleichen. Hier ist eine Referenz; https://docs.oracle.com/javase/j/docs/technotes/tools/windows/javap.html – DwB

+0

Nun, ich denke nicht, dass diese Frage eine Frage auf Studentenebene ist, aber naja, ich fragte mich, ob jemand die Antwort bereits kennt dazu, anstatt mir zu sagen, dass es möglich ist, auf den Ausgangsbytecode zu schauen, von dem ich weiß, dass ich könnte. und außerdem würde ich die Ausgaben für Android auch betrachten müssen. Das kann ein höllisches Projekt sein, um diese Frage selbst zu beantworten. –

+1

Kann der Compiler wissen, dass 'a.bar()' immer den gleichen Wert für ein gegebenes 'a' liefert? –

Antwort

2

Ich habe zwei Beispiele versucht, wie Sie für Ihre Frage tun. Auf dieser Grundlage muss ich sagen, dass der zweite Ansatz besser wäre. (Obwohl Ich erwäge nicht 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); 
     } 
    } 
} 

nach dem Kompilieren und dann wieder decompiling ich diese bekam.

* 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); 
     } 
    } 
} 

nach dem Kompilieren und dann wieder decompiling ich diese bekam.

/* 
* 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

Nach Kommentaren zu sehen und auf sie zu antworten haben, wurde mir klar, dass selbst wenn der Compiler in Verständnis tauchen würde a.bar() ‚s-Code, es wäre unmöglich, zu garantieren, dass a.bar() Ergebnis nicht in einem Multi-Threaded ändern würde Enviroment wie es ist in Java, auch in Verbindung mit Reflection wenn bar ist ein Mitglied Feld kann es durch Reflexionen ändern und der Compiler würde nicht wissen, dass im Voraus, die eine einfache return bar; unvorhersehbar sein kann.

So scheint es mir rational zu sein, diese Optimierung in meinem Code zu verwenden.

+0

"garantiert, dass' a.bar() 'Ergebnis in einer Multithread-Umgebung nicht geändert wird" - wenn die Methode keine Synchronisation oder Freunde enthält (wie 'volatile'), kann der Compiler andere Threads ignorieren. Ohne Synchronisierung garantiert das JMM keine Sichtbarkeit von Änderungen, die von anderen Threads vorgenommen wurden, und weder die Hardware noch die Hardware garantieren irgendetwas (denken Sie an Core-Caches). +++ Das könnte eine nützliche Optimierung für Android sein, die nicht so intelligent ist wie die JVM der Serverklasse. – maaartinus

+0

Beachten Sie, dass die Änderung an dem Element zwischen Iterationen auf Foos-Objekten auftreten kann, volatile wird in diesem Fall nicht helfen und Synchronisierung kann nur helfen, wenn es richtig gemacht wird. Ich finde es schwer zu glauben, dass ein Compiler in allen Fällen die Korrektheit Ihrer Synchronisation analysieren könnte ... –

+0

Was ist, wenn 'a.bar()' eine 'final' Methode ist, die ein' final' Feld zurückgibt? – shmosel

0

überraschend, da kann es scheint, versuchen:

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()); 
} 

nur ein kleines zwicken, versuchen Sie es mal

+0

Was erhoffen Sie sich davon? Übrigens. Sie haben auch einen Syntaxfehler. – Hulk