2014-05-04 50 views
6

下面兩個代碼片段在性能上有什麼不同嗎?這兩個循環之間有什麼區別嗎?

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

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

對於我來說,我覺得第二個是更好的,但它是更長的時間。第一個更短,但我不確定它是否更快。我不確定,但對我來說,似乎每次循環迭代時,都會調用auth.getProjects。那不是嗎?

+0

它只被調用一次,但第二個版本太冗長了。你正在做的是通過一個列表。一條線就夠了。 – Navin

回答

10

編輯@StephenC是對的,JLS是一個更好的地方找到這種性質的答案。語言規範中有一個link to the enhanced for loop。在那裏你會發現它有幾種不同類型的for語句,但它們都不會超過1次調用這個方法。


簡單測試表明,該方法只調用一次

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

輸出:

get the strings 
one 
two 
three 

所以基本上他們是同樣的事情。如果你想在for循環之外使用數組,唯一可能對2nd有利的是。


編輯

你讓我好奇的處理上面我會這樣使用的代碼Java編譯器如何反編譯的類文件和繼承人的結果是什麼

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

,你可以看到編譯器簡單地將你的for循環重構爲一個標準循環!它也進一步證明,實際上它們在編譯器完成之後完全相同。

+0

謝謝。現在我清楚地知道幕後究竟發生了什麼。 – hamid

+3

*「簡單的測試表明該方法只被調用一次」* - 簡單的測試可能會誤導......因爲你不知道你應該測試什麼。更好的方法是閱讀JLS。 –

+0

+1對你的好解釋 –

-1

第二個是更好的性能明智,它不是多次創建一個變量。因此,每次都會調用auth.getProjects。

+0

不正確。保存'auth.getProjects()'的局部變量被創建並分配一次。看我的答案。 –

+0

對不起,我在晚上寫了xD – loafy

3

第二個例子中還有一些操作。不是直接使用方法引用數組,而是聲明一個新的引用變量,將其存儲在該新變量中,然後引用新變量。

您可以使用ASM Bytecode Outline查看字節碼。

但是,這是微觀優化。如果你需要一個新的參考變量,然後創建一個。如果你不需要,保存工作,不要創建一個新的工作。

+1

您的意思是「第二個例子」而不是「第一個例子」?第二個例子是聲明一個新的引用變量的例子。儘管如果引用變量沒有在函數的其他地方使用,大多數編譯器都可能在這兩種情況下使用寄存器。 –

+1

@WarrenDew是的,我編輯了我的答案,謝謝你指出, –

3

假設你的意思是in你的意思是:,性能沒有區別;他們都做同樣的事情。即使在第一個例子中,auth.getProjects()也只執行一次;它不能被執行多次,因爲如果是的話,那麼for迭代將不得不每次都開始,這不是它的工作原理。

我推薦使用你發現的版本更清晰。

-1

兩者都與上面相同。

但是,做一件事情使用第一個程序,並在for循環之後,取消引用項目變量爲 - projects = null; 否則,它會保持活着的完整方法生活,並會消耗不必要的記憶。

+0

不正確。 getProjects方法不被稱爲「在循環中」。它在循環的開始被調用一次。 –

+0

根據正確答案進行更改。 – 53by97

2

沒有區別。

根據該JLS 14.14.2,增強for環路(用於陣列型)等效於常規for循環具有以下圖案:

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

如果我們代替您的第一個例子:

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

我們得到:

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

爲您的第二個例子:

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

我們得到:

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

代碼的兩個版本顯然是等價的,並假設javac或JIT能夠優化掉多餘的局部變量$a的,這兩個版本應該執行相同的操作。

請注意,在兩種情況下,本地變量$a僅在循環開始時創建一次。

相關問題