第一部分:
For-each Loop
Purpose
The basic for loop was extended in Java 5 to make iteration over arrays and other collections more convenient. This newer for statement is called the enhanced for or for-each (because it is called this in other programming languages). I've also heard it called the for-in loop.
Use it in preference to the standard for loop if applicable (see last section below) because it's much more readable.
Series of values. The for-each loop is used to access each successive value in a collection of values.
Arrays and Collections. It's commonly used to iterate over an array or a Collections class (eg, ArrayList).
Iterable<E>. It can also iterate over anything that implements the Iterable<E> interface (must defineiterator()
method). Many of the Collections classes (eg, ArrayList
) implement Iterable<E>, which makes thefor-each loop very useful. You can also implement Iterable<E> for your own data structures.
General Form
The for-each and equivalent for statements have these forms. The two basic equivalent forms are given, depending one whether it is an array or an Iterable that is being traversed. In both cases an extra variable is required, an index for the array and an iterator for the collection.
這里我們只要知道下面的事實就好了:
- For-each語法內部,對collection是用nested iteratoration來實現的,對數組是用下標遍歷來實現。
- Java 5 及以上的編譯器隱藏了基於iteration和下標遍歷的內部實現。(注意,這里說的是“Java編譯器”或Java語言對其實現做了隱藏,而不是某段Java代碼對其實現做了隱藏,也就是說,我們在任何一段JDK的Java代碼中都找不到這里被隱藏的實現。這里的實現,隱藏在了Java 編譯器中,我們可能只能像這篇帖子中說的那樣,查看一段For-each的Java代碼編譯成的字節碼,從中揣測它到底是怎么實現的了)
下面對“For-each”和“其對等的iteration/index實現”的對比再簡潔明了不過了。
For-each loop | Equivalent for loop |
---|---|
for (type var : arr) { body-of-loop } |
for (int i = 0; i < arr.length; i++) { type var = arr[i]; body-of-loop } |
for (type var : coll) { body-of-loop } |
for (Iterator<type> iter = coll.iterator(); iter.hasNext(); ) { type var = iter.next(); body-of-loop } |
Example - Adding all elements of an array
Here is a loop written as both a for-each loop and a basic for loop.
double[] ar = {1.2, 3.0, 0.8}; int sum = 0; for (double d : ar) { // d gets successively each value in ar. sum += d; }
And here is the same loop using the basic for. It requires an extra iteration variable.
double[] ar = {1.2, 3.0, 0.8}; int sum = 0; for (int i = 0; i < ar.length; i++) { // i indexes each element successively. sum += ar[i]; }
Where the for-each is appropriate
Altho the enhanced for loop can make code much clearer, it can't be used in some common situations.
使用For-each時對collection或數組中的元素不能做賦值操作
- Only access. Elements can not be assigned to, eg, not to increment each element in a collection.
同時只能遍歷一個collection或數組,不能同時遍歷多余一個collection或數組
- Only single structure. It's not possible to traverse two structures at once, eg, to compare two arrays.
遍歷過程中,collection或數組中同時只有一個元素可見,即只有“當前遍歷到的元素”可見,而前一個或后一個元素是不可見的。
- Only single element. Use only for single element access, eg, not to compare successive elements.
只能正向遍歷,不能反向遍歷(相比之下,C++ STL中還有reverse_iterator, rbegin(), rend()之類的東西,可以反向遍歷)
- Only forward. It's possible to iterate only forward by single steps.
如果要兼容Java 5之前的Java版本,就不能使用For-each
- At least Java 5. Don't use it if you need compatibility with versions before Java 5.
此文章來自:http://blog.csdn.net/yasi_xi/article/details/25482173#t1
第二部分:
先看一下這么一段代碼:
public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("111"); list.add("222"); for (String str : list) { System.out.println(str); } }
用foreach循環去遍歷這個list,結果就不說了,都知道。看一下Java是如何處理這個foreach循環的,javap反編譯一下:
d:\MyEclipse\TestArticle\bin\test29>javap -verbose TestMain.class
反編譯出來的內容很多,有類信息、符號引用、字節碼信息,截取一段信息:
1 public static void main(java.lang.String[]); 2 flags: ACC_PUBLIC, ACC_STATIC 3 Code: 4 stack=2, locals=4, args_size=1 5 0: new #16 // class java/util/ArrayList 6 3: dup 7 4: invokespecial #18 // Method java/util/ArrayList."<in 8 it>":()V 9 7: astore_1 10 8: aload_1 11 9: ldc #19 // String 111 12 11: invokeinterface #21, 2 // InterfaceMethod java/util/List. 13 add:(Ljava/lang/Object;)Z 14 16: pop 15 17: aload_1 16 18: ldc #27 // String 222 17 20: invokeinterface #21, 2 // InterfaceMethod java/util/List. 18 add:(Ljava/lang/Object;)Z 19 25: pop 20 26: aload_1 21 27: invokeinterface #29, 1 // InterfaceMethod java/util/List. 22 iterator:()Ljava/util/Iterator;
看不懂沒關系,new、dup、invokespecial這些本來就是字節碼指令表內定義的指令,虛擬機會根據這些指令去執行指定的C++代碼,完成每個指令的功能。關鍵看到21、22這兩行就可以了,看到了一個iterator,所以得出結論:在編譯的時候編譯器會自動將對for這個關鍵字的使用轉化為對目標的迭代器的使用,這就是foreach循環的原理。進而,我們再得出兩個結論:
1、ArrayList之所以能使用foreach循環遍歷,是因為ArrayList所有的List都是Collection的子接口,而Collection是Iterable的子接口,ArrayList的父類AbstractList正確地實現了Iterable接口的iterator方法。之前我自己寫的ArrayList用foreach循環直接報空指針異常是因為我自己寫的ArrayList並沒有實現Iterable接口
2、任何一個集合,無論是JDK提供的還是自己寫的,只要想使用foreach循環遍歷,就必須正確地實現Iterable接口
實際上,這種做法就是23中設計模式中的迭代器模式。
數組呢?
上面的講完了,好理解,但是不知道大家有沒有疑問,至少我是有一個疑問的:數組並沒有實現Iterable接口啊,為什么數組也可以用foreach循環遍歷呢?先給一段代碼,再反編譯:
public static void main(String[] args) { int[] ints = {1,2,3,4,5}; for (int i : ints) System.out.println(i); }
同樣反編譯一下,看一下關鍵的信息:
1 0: iconst_2 2 1: newarray int 3 3: dup 4 4: iconst_0 5 5: iconst_1 6 6: iastore 7 7: dup 8 8: iconst_1 9 9: iconst_2 10 10: iastore 11 11: astore_1 12 12: aload_1 13 13: dup 14 14: astore 5 15 16: arraylength 16 17: istore 4 17 19: iconst_0 18 20: istore_3 19 21: goto 39 20 24: aload 5 21 26: iload_3 22 27: iaload 23 28: istore_2 24 29: getstatic #16 // Field java/lang/System.out:Ljav 25 a/io/PrintStream; 26 32: iload_2 27 33: invokevirtual #22 // Method java/io/PrintStream.prin 28 tln:(I)V 29 36: iinc 3, 1 30 39: iload_3 31 40: iload 4 32 42: if_icmplt 24 33 45: return
這是完整的這段main函數對應的45個字節碼指令,因為這涉及一些壓棧、出棧、推送等一些計算機原理性的內容且對於這些字節碼指令的知識的理解需要一些C++的知識,所以就不解釋了。簡單對照字節碼指令表之后,我個人對於這45個字節碼的理解是Java將對於數組的foreach循環轉換為對於這個數組每一個的循環引用。
第二部分內容來自:http://www.cnblogs.com/xrq730/p/4868465.html