2015-02-09 45 views
3

我正在試圖找到for-each循環的工作,當我做一個函數調用。請參見下面的代碼,for-each循環如何在JAVA內部工作?

public static int [] returnArr() 
{ 
    int [] a=new int [] {1,2,3,4,5}; 
    return a; 
} 

public static void main(String[] args) 
{ 
    //Version 1 
    for(int a : returnArr()) 
    { 
     System.out.println(a); 
    } 

    //Version 2 
    int [] myArr=returnArr(); 
    for(int a : myArr) 
    { 
     System.out.println(a); 
    } 
} 

在版本1中,我打電話returnArr()在for-each循環和在第2版,我顯式調用returnArr()方法並將其分配給一個數組和方法然後遍歷它。兩種方案的結果都相同。我想知道哪個更有效,爲什麼。

我認爲版本2會更有效率,因爲我沒有在每次迭代中調用方法。但令我驚訝的是,當我使用版本1調試代碼時,我看到方法調用只發生一次!

任何人都可以請解釋它是如何工作的當我編碼複雜的對象時,哪個更有效/更好

+0

那麼,您親眼看到調試第一個版本時沒有區別。 – Eran 2015-02-09 07:11:05

+0

因此,在代碼效率方面,兩個版本沒有區別? – Abhishek 2015-02-09 07:37:09

回答

1

我不打算從Java Language Specification複製粘貼,像以前的一個答案並沒有,而是以一種可讀格式來解釋規範。

考慮下面的代碼:

for (T x : expr) { 
    // do something with x 
} 

如果expr計算結果爲陣列型像您的情況下,語言規範規定所得到的字節碼將是相同的:

T[] arr = expr; 
for (int i = 0; i < arr.length; i++) { 
    T x = arr[i]; 
    // do something with x 
} 

的不同之處在於變量arri對您的代碼或調試器不可見。這就是爲什麼開發時,第二個版本可能更有用:您的返回值存儲在調試器可訪問的變量中。

在您的第一個版本中,expr只是函數調用,而在第二個版本中,您聲明另一個變量並將函數調用的結果分配給該函數,然後使用該變量作爲expr。我希望他們在性能上沒有可衡量的差異,因爲第二個版本中的附加變量賦值應該由JIT編譯器進行優化,除非您還在別處使用它。

4

Java Language Specification示出底層的彙編

L1 ... Lm是標籤的(可能爲空)序列立即 前述增強的for語句。

增強的for語句相當於 形式基本for聲明:

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

其中Expression是在增強for語句:(您returnArr())的右手邊。在這兩種情況下,它只被評估一次:在版本1中,作爲增強for語句的一部分;在版本2中,因爲其結果被分配給一個變量,然後在增強的for語句中使用該變量。

+0

那麼,編碼時哪個版本更高效呢?他們兩個是相同的? – Abhishek 2015-02-09 07:34:27

+2

版本1可能是更有效的一個微不足道的數量(一個更少的變量賦值在循環之前),而版本2更加調試友好(你可以看到函數的返回值在一個可訪問的變量)。 – Wormbo 2015-02-09 07:40:42

+0

@Wormbo感謝您的見解:) – Abhishek 2015-02-10 06:05:25

1

編譯器爲只調用一次方法returnArr()編譯時優化 :)

字節代碼:

public static void main(java.lang.String[]); 
    descriptor: ([Ljava/lang/String;)V 
    flags: ACC_PUBLIC, ACC_STATIC 
    Code: 
    stack=2, locals=6, args_size=1 

** case -1 start *** 
     0: invokestatic #20     // Method returnArr:()[I --> called only once. 
     3: dup 
     4: astore  4 
     6: arraylength 
     7: istore_3 
     8: iconst_0 
     9: istore_2 
     10: goto   28 
     13: aload   4 --> loop start 
     15: iload_2 
     16: iaload 
     17: istore_1 
     18: getstatic  #22     // Field java/lang/System.out:Ljav 
/io/PrintStream; 
     21: iload_1 
     22: invokevirtual #28     // Method java/io/PrintStream.prin 
ln:(I)V 
     25: iinc   2, 1 
     28: iload_2 
     29: iload_3 
     30: if_icmplt  13 


***case -2 start**** 

     33: invokestatic #20     // Method returnArr:()[I 
     36: astore_1 
     37: aload_1 
     38: dup 
     39: astore  5 
     41: arraylength 
     42: istore  4 
     44: iconst_0 
     45: istore_3 
     46: goto   64 
     49: aload   5 --> loop start case 2 
     51: iload_3 
     52: iaload 
     53: istore_2 
     54: getstatic  #22     // Field java/lang/System.out:Ljav 
/io/PrintStream; 
     57: iload_2 
     58: invokevirtual #28     // Method java/io/PrintStream.prin 
ln:(I)V 
     61: iinc   3, 1 
     64: iload_3 
     65: iload   4 
     67: if_icmplt  49 
     70: return 

注:我使用JDK 8

+0

是的,我在觀察調試過程中的一次性調用。 :)仍然我想知道哪個版本更好,當你的代碼? – Abhishek 2015-02-09 07:35:47

+0

@Abhishek - *編譯器*或* JIT *將優化您的代碼。如果你從邏輯的角度*問,那麼版本2是好的。 – TheLostMind 2015-02-09 08:33:30

+0

明白了。謝謝 :) – Abhishek 2015-02-10 06:04:54