for 、foreach 、iterator 三種遍歷方式的比較


習慣用法

for、foreach循環、iterator迭代器都是我們常用的一種遍歷方式,你可以用它來遍歷任何東西:包括數組、集合等

for 慣用法:

List<String> list = new ArrayList<String>();
String[] arr = new String[]{"1,2,3,4"};
for(int i = 0;i < arr.length;i++){
    System.out.println(arr[i]);
}
for(int i = 0;i < list.size();i++){
    System.out.println(list.get(i));
}

foreach 慣用法:

String[] arr = new String[]{"1,2,3,4"};
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for(String str : arr){
    System.out.println(str);
}
for (String item : list) {
    System.out.println(item);
}

Iterator 慣用法:

Iterator<String> it = list.iterator();
while (it.hasNext()){
    System.out.println(it.next());
}

速度對比

性能是我們選取某一種技術手段的一種考慮方式,且看這三種遍歷方式的速度對比

List<Long> list = new ArrayList<Long>();
long maxLoop = 2000000;
for(long i = 0;i < maxLoop;i++){
    list.add(i);
}

// for循環
long startTime = System.currentTimeMillis();
for(int i = 0;i < list.size();i++){
    ;
}
long endTime = System.currentTimeMillis();
System.out.println(endTime - startTime + "ms");

// foreach 循環
startTime = System.currentTimeMillis();
for(Long lon : list){
    ;
}
endTime = System.currentTimeMillis();
System.out.println(endTime - startTime + "ms");

// iterator 循環
startTime = System.currentTimeMillis();
Iterator<Long> iterator = list.iterator();
while (iterator.hasNext()) {
    iterator.next();
}
endTime = System.currentTimeMillis();
System.out.println(endTime - startTime + "ms");

4ms
16ms
9ms

由以上得知,for()循環是最快的遍歷方式,隨后是iterator()迭代器,最后是foreach循環

remove操作三種遍歷方式的影響

for循環的remove

List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for(int i = 0;i < list.size();i++){
    if("2".equals(list.get(i))){
        System.out.println(list.get(i));
        list.remove(list.get(i));
    }
}

for循環可以直接進行remove,不會受到任何影響。

foreach 中的remove

List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
for (String item : list) {
    if ("2".equals(item)) {
        System.out.println(item);
        list.remove(item);
    }
}

你覺得這段代碼的正確輸出是什么?我們一起來探究一下

當我執行一下這段代碼的時候,出現了以下的情況

由以上異常情況的堆棧信息得知,程序出現了並發修改的異常,為什么會這樣?我們從錯誤開始入手,

at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)

也就是這行代碼,找到這行代碼的所在地

final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}

你會好奇, modCount 和 expectedModCount 是什么變量?在我對 ArrayList 相關用法那篇文章中有比較詳細的解釋。我大致說明一下: modCount 相當於是程序所能夠進行修改 ArrayList 結構化的一個變量,怎么理解?看幾個代碼片段

你能夠從中獲取什么共性的特征呢?沒錯,也就是涉及到其中關於ArrayList的 容量大小 和 __元素個數__的時候,就會觸發modCount 的值的變化

expectedModCount這個變量又是怎么回事?從ArrayList 源碼可知,這個變量是一個局部變量,也就是說每個方法內部都有expectedModCount 和 modCount 的判斷機制,進一步來講,這個變量就是 預期的修改次數

先拋開這個不談,我們先來談論一下foreach(增強for循環)本身。

增強for循環是Java給我們提供的一個語法糖,如果將以上代碼編譯后的class文件進行反編譯(使用jad工具)的話,可以得到以下代碼:

terator iterator = item.iterator();

也就是說,其實foreach 每次循環都調用了一次iterator的next()方法

因此才會有這個堆棧信息:

at java.util.ArrayList$Itr.next(ArrayList.java:859)

下面我們來嘗試分析一下這段代碼報錯的原因:

1、第一次 以 “1”的值進入循環,"1" != "2", 執行下一次循環

2、第二次循環以"2"的值進入,判斷相等,執行remove()方法(注意這個remove方法並不是 iterator的remove(),而是ArrayList的remove()方法),導致modCount++

3、再次調用next()的時候,modCount != expectedModCount ,所以拋出異常

Iterator迭代器的remove

使用迭代器進行遍歷還有很多需要注意的地方:

正確的遍歷

List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> it = list.iterator();
while (it.hasNext()){
    System.out.println(it.next());
    it.remove();
}

這是一種正確的寫法,如果輸出語句和 remove()方法互換順序怎么樣呢?

錯誤的遍歷 —— next() 和 remove() 執行順序的問題

List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> it = list.iterator();
while (it.hasNext()){
    it.remove();
    System.out.println(it.next());
}

執行程序輸出就會報錯:

Exception in thread "main" java.lang.IllegalStateException
	at java.util.ArrayList$Itr.remove(ArrayList.java:872)
	at test.SimpleTest.main(SimpleTest.java:46)

這又是為什么? 還是直接從錯誤入手:

定位到錯誤的位置

at java.util.ArrayList$Itr.remove(ArrayList.java:872)

發現如果 lastRet 的值小於 0就會拋出非法狀態的異常,這個lastRet是什么?

且看定義:

lastRet的賦值場景

由上面代碼可以看出,當你執行next()方法的時候, lastRet 賦值為i,所以這個elementData[]中的下標最小是0,所以這個時候lastRet 最小的值是0, 那么只有當執行remove()方法的時候,lastRet的值賦值為-1,也就是說,你必須先執行一次next方法,再執行一次remove方法,才能夠保證程序的正確運行。

**錯誤的遍歷 —— 使用Arrays.asList() **

List<String> list = Arrays.asList("1","2","3");
Iterator<String> it = list.iterator();
while (it.hasNext()){
  System.out.println(it.next());
  it.remove();
}

這段代碼執行之后的輸出是怎樣的呢?

1
Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.AbstractList.remove(AbstractList.java:161)
	at java.util.AbstractList$Itr.remove(AbstractList.java:374)
	at test.SimpleTest.main(SimpleTest.java:50)

很不幸,這段代碼也拋出了異常,直接從錯誤處入手發現,這個remove()方法調用的是AbstractList中的remove方法,跟進入發現有一段代碼

remove()方法:

也就是說,只要這段代碼執行了,都會報錯,拋出異常

后記:

上述文章主要介紹了 for循環、foreach 循環、iterator 迭代器遍歷元素的速度大小的比較

還介紹了各自遍歷過程中 對remove操作的影響。

如果你有什么問題或者好的建議,歡迎你與我一起討論


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM