標籤:而不是 rate forms 注意 簡單 實現原理 extra tail href
第一部分:
For-each LoopPurpose
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一定要注意For-each不是萬能的,下面的場合是不適宜使用For-each的
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_110 8: aload_111 9: ldc #19 // String 11112 11: invokeinterface #21, 2 // InterfaceMethod java/util/List.13 add:(Ljava/lang/Object;)Z14 16: pop15 17: aload_116 18: ldc #27 // String 22217 20: invokeinterface #21, 2 // InterfaceMethod java/util/List.18 add:(Ljava/lang/Object;)Z19 25: pop20 26: aload_121 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迴圈直接報null 指標異常是因為我自己寫的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_210 10: iastore11 11: astore_112 12: aload_113 13: dup14 14: astore 515 16: arraylength16 17: istore 417 19: iconst_018 20: istore_319 21: goto 3920 24: aload 521 26: iload_322 27: iaload23 28: istore_224 29: getstatic #16 // Field java/lang/System.out:Ljav25 a/io/PrintStream;26 32: iload_227 33: invokevirtual #22 // Method java/io/PrintStream.prin28 tln:(I)V29 36: iinc 3, 130 39: iload_331 40: iload 432 42: if_icmplt 2433 45: return
這是完整的這段main函數對應的45個位元組碼指令,因為這涉及一些壓棧、出棧、推送等一些電腦原理性的內容且對於這些位元組碼指令的知識的理解需要一些C++的知識,所以就不解釋了。簡單對照位元組碼指令表之後,我個人對於這45個位元組碼的理解是Java將對於數組的foreach迴圈轉換為對於這個數組每一個的循環參考。
第二部分內容來自:http://www.cnblogs.com/xrq730/p/4868465.html
Java:foreach實現原理