2016-05-16 26 views
5

考慮以下幾點:這是java中循環的有用優化嗎?

1.

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

和:

2.

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

是這種優化的真正的幫助或將編譯器自動執行,無論如何?

如果bar()是一個getter,你的回答會改變嗎? (例如getBar())

如果我的目標是Android開發,您的答案會改變嗎?

+2

工作,爲學生:編譯兩個版本,並比較字節碼輸出。這裏是一個參考; https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javap.html – DwB

+0

嗯,我不認爲這個問題是一個學生水平的問題,但哦,我想知道如果任何人已經知道答案而不是告訴我可以看看輸出字節碼,我知道我可以。而且,我也必須看看android的輸出。這可能是我自己回答這個問題的一個項目。 –

+1

編譯器是否可以知道'a.bar()'將始終爲給定的'a'返回相同的值? –

回答

2

我已經嘗試了兩個例子,因爲你在爲你的問題做。在此基礎上,我必須說第二種方法會更好。 (雖然我不考慮多線程

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

編譯後,然後再反編譯我得到了這一點。

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

後重新編譯,然後反編譯我得到了這一點。

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

看到的評論,不得不回答他們後,我意識到,即使編譯器會潛入理解a.bar()的代碼,這將是不可能的機制保障是a.bar()結果不會在多線程改變環境,如它是在Java中,還與Reflection結合,如果bar是成員字段,它可以通過反射來改變,編譯器不會提前知道可能導致簡單的return bar;不可預知。

因此,目前,我覺得在我的代碼中繼續使用這個優化是很合理的。

+0

「保證'a.bar()'結果不會在多線程環境中改變」 - 除非方法包含某些同步或朋友(如'volatile'),編譯器可以自由忽略其他線程。如果沒有同步,JMM保證不會看到其他線程所做的更改,HW也不保證任何內容(按每個核心緩存思考)。這就是說,它可能是Android上的一個有用的優化,它不像服務器類JVM那麼聰明。 – maaartinus

+0

請注意,對foos對象的迭代之間可能會發生對成員的更改,在這種情況下volatile不會起作用,並且只有在正確完成後才能進行同步。我發現很難相信編譯器可以在任何情況下分析你的同步的正確性...... –

+0

如果'a.bar()'是一個返回final字段的final方法會怎麼樣? – shmosel

0

令人驚訝的,因爲它可能似乎嘗試:

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

只是一個小的調整,給它一個嘗試

+0

你希望通過這個獲得什麼?順便說一句。你也有一個語法錯誤。 – Hulk