天天看點

Java文法糖之foreach

  文法糖是一種幾乎每種語言或多或少都提供過的一些友善程式員開發代碼的文法,它隻是編譯器實作的一些小把戲罷了,編譯期間以特定的位元組碼或者特定的方式對這些文法做一些處理,開發者就可以直接友善地使用了。這些文法糖雖然不會提供實質性的功能改進,但是它們或能提高性能、或能提升文法的嚴謹性、或能減少編碼出錯的機會。Java提供給了使用者大量的文法糖,比如泛型、自動裝箱、自動拆箱、foreach循環、變長參數、内部類、枚舉類、斷言(assert)等。

  本篇主要是講解foreach,foreach的文法經過編譯之後解析成什麼呢?

  首先來看一個例子:

  對這個類進行反編譯:

  打開f1.txt,結果如下所示:

  反編譯出來的内容很多,看不懂也沒關系,關鍵看到Iterator這個标志,其實在對有實作Iterable接口的對象采用foreach文法糖的話,編譯器會将這個for關鍵字轉化為對目标的疊代器使用。

  上面的代碼會被轉化為如下的代碼:

注:如果要想使自己自定義的類可以采用foreach文法糖就必須實作Iterable接口。

  細心的朋友可能會注意到,java中的數組也可以采用foreach的文法糖,但是數組并沒有實作Iterable接口呀。

  下面再舉一個例子:

  反編譯結果:

  看不懂?同樣沒關系~這裡可以注意到數組的foreach文法糖并沒有采用Iterable實作轉換。如上面的資訊隻是涉及一些壓棧出站的内容。真正的解析結果如下所示:

  可以看到對于數組而言,其實就是轉換為普通的周遊而已;

  關于foreach文法糖的資訊就這樣結束了嚒? 顯然沒有!

  對于實作RandomAccess接口的集合比如ArrayList,應當使用最普通的for循環而不是foreach循環來周遊,是以第一個例子中有欠妥之處。

  首先看一下jdk1.7中對RandomAccess接口的定義:

java.util

Interface RandomAccess

All Known Implementing Classes:

ArrayList, AttributeList, CopyOnWriteArrayList, RoleList, RoleUnresolvedList, Stack, Vector

public interface RandomAccess

Marker interface used by List implementations to indicate that they support fast (generally constant time) random access. The primary purpose of this interface is to allow generic algorithms to alter their behavior to provide good performance when applied to either random or sequential access lists.

The best algorithms for manipulating random access lists (such as ArrayList) can produce quadratic behavior when applied to sequential access lists (such as LinkedList). Generic list algorithms are encouraged to check whether the given list is an instanceof this interface before applying an algorithm that would provide poor performance if it were applied to a sequential access list, and to alter their behavior if necessary to guarantee acceptable performance.

It is recognized that the distinction between random and sequential access is often fuzzy. For example, some List implementations provide asymptotically linear access times if they get huge, but constant access times in practice. Such a List implementation should generally implement this interface. As a rule of thumb, a List implementation should implement this interface if, for typical instances of the class, this loop:

for (int i=0, n=list.size(); i < n; i++)

list.get(i);

runs faster than this loop:

for (Iterator i=list.iterator(); i.hasNext(); )

i.next();

This interface is a member of the Java Collections Framework.

從以下版本開始:

1.4

  看不懂英文也沒關系,我來解釋一下最後一句加粗的話:實際經驗表明,實作RandomAccess接口的類執行個體,假如是随機通路的,使用普通for循環效率将高于使用foreach循環;反過來,如果是順序通路的,則使用Iterator會效率更高。

  可以使用類似如下的代碼作判斷:

其實如果看過ArrayList源碼的同學也可以注意到:ArrayList底層是采用數組實作的,如果采用Iterator周遊,那麼還要建立許多指針去執行這些值(比如next();hasNext())等,這樣必然會增加記憶體開銷以及執行效率。

  

附:javap(反彙編指令)詳解   javap是JDK自帶的反彙編器,可以檢視java編譯器為我們生成的位元組碼。通過它,我們可以對照源代碼和位元組碼,進而了解很多編譯器内部的工作。   文法:   javap [ 指令選項 ] class…   javap 指令用于解析類檔案。其輸出取決于所用的選項。若沒有使用選項,javap 将輸出傳遞給它的類的 public 域及方法。javap 将其輸出到标準輸出裝置上。 指令選項 -help 輸出 javap 的幫助資訊。 -l 輸出行及局部變量表。 -b 確定與 JDK 1.1 javap 的向後相容性。 -public 隻顯示 public 類及成員。 -protected 隻顯示 protected 和 public 類及成員。 -package 隻顯示包、protected 和 public 類及成員。這是預設設定。 -private 顯示所有類和成員。 -J[flag] 直接将 flag 傳給運作時系統。 -s 輸出内部類型簽名。 -c 輸出類中各方法的未解析的代碼,即構成 Java 位元組碼的指令。 -verbose 輸出堆棧大小、各方法的 locals 及 args 數,以及class檔案的編譯版本 -classpath[路徑] 指定 javap 用來查找類的路徑。如果設定了該選項,則它将覆寫預設值或 CLASSPATH 環境變量。目錄用冒号分隔。 - bootclasspath[路徑] 指定加載自舉類所用的路徑。預設情況下,自舉類是實作核心 Java 平台的類,位于 jrelibt.jar 和 jrelibi18n.jar 中。 -extdirs[dirs] 覆寫搜尋安裝方式擴充的位置。擴充的預設位置是 jrelibext。