Java 的三種迴圈:foreach,Iterator 和 classic for loop
不得不說,java語言在提供了這三種迴圈方式帶來靈活性的同時,同時也將一些“混亂”引入了進來。
這裡的“混亂”並不是真正意義上的混亂,而是由於沒有統一的風格而帶來使用習慣的問題——想象一下,如果同一個專案中這三種都有人用,閱讀起來真是五味雜陳啊。
有人要問了,那麼,這三種到底哪種好呢?
在回答這個問題之前,能告訴我“好”的定義是什麼嗎?
我所認為的好,無非2點——1、程式碼簡潔;2、效能高效
接下來,我們對這3種for迴圈方式一一評估。
Classic for loop
首先,來看看classic for loop.
<pre name="code" class="java">List<String> birds = new ArrayList<String>() { { add("magpie"); add("crow"); add("emu"); } }; for (int i = 0; i < birds.size(); i++) { String bird = birds.get(i); }
這種方式,程式碼風格還好,可惜的是,有個隱藏的效能問題。
對於List介面的眾多實現類來講,並不是每個實現的get(i)都是O(1)時間的。比如LinkedList的時間複雜度就是O(n)。
這樣,每次呼叫get方法的效能從固定時間變為了隨著n增長而增長的線性時間。
(這裡,我不打算討論list.size()方法的效能,如果僅僅從LinkedList和ArrayList這樣常用的方法來看,它的效能損失可以忽略不計,除非是追求極致)
(圖片引用自:ArrayList vs. LinkedList vs. Vector [1])
換句話講,如果上面的List實現換成LinkedList,這樣的程式碼就可能存在隱藏的效能問題。(當List比較大且頻繁呼叫的情況下)
而恰恰Java又是提倡隱藏實現細節的語言,使用者往往並不知道傳入的List實現究竟是哪一種。所以……可能,僅僅是可能,一個隱藏的效能地雷埋下了。
Iterator
現在,來看看 iterator for 迴圈。
上面那個例子,稍加改動就成為了下面這樣:
List<String> birds = new ArrayList<String>() { { add("magpie"); add("crow"); add("emu"); } }; for (Iterator<String> itr = birds.iterator(); itr.hasNext();) { String bird = itr.next(); }
從效能角度來看,這種方式還好,獲取每個元素都是固定時間,但是,從程式碼風格來看,略顯複雜了。
不過,iterator有個優點,就是可以在迴圈體內刪除列表中的元素(可能成功-依賴List的具體實現),而其他的2種方式不行。
List<String> birds = new ArrayList<String>() {
{
add("magpie");
add("crow");
add("emu");
}
};
// 下面的for迴圈執行後,birds列表將被清空
for (Iterator<String> itr = birds.iterator(); itr.hasNext();) {
String bird = itr.next();
itr.remove();
}
而下面這種方式是錯誤的
List<String> birds = new ArrayList<String>() {
{
add("magpie");
add("crow");
add("emu");
}
};
// 下面的for迴圈執行時將會丟擲異常
for (String bird : birds) {
birds.remove(bird);
}
Foreach
最後,來看看用JDK5引入的神器,foreach迴圈。
List<String> birds = new ArrayList<String>() {
{
add("magpie");
add("crow");
add("emu");
}
};
for (String bird : birds) {
}
從程式碼風格上來看,它最簡潔。那麼效能如何呢?
其實,對於集合來說,它只是Iterator迴圈的包裝(甜頭),編寫程式碼的時候簡化了程式碼,而編譯的時候依然是用Iterator實現的。
對於陣列型別,還是用的classic for loop實現。
所以,效能也是最優的。
對比一下這三種方式,我們可以得出結論:
簡潔性 | 效能 | |
classic for loop | OK | 可能會差 |
Iterator | 差 | 好 |
foreach | 好 | 好 |
所以,如果可以,儘量選擇使用foreach迴圈,簡潔且高效。
引用: