foreach循環里不能remove/add元素的原理


foreach循環

​    foreach循環(Foreach loop)是計算機編程語言中的一種控制流程語句,通常用來循環遍歷數組或集合中的元素。Java語言從JDK 1.5.0開始引入foreach循環。在遍歷數組、集合方面,foreach為開發人員提供了極大的方便。通常也被稱之為增強for循環。

​    在日常開發中,foreach循環用的非常多,但是有一點要非常小心,就是不能在這個循環里對數組或者集合里的元素進行remove或者add操作,否則會拋出java.util.ConcurrentModificationException


## 開發規范

​    在阿里巴巴java開發規范手冊中有這樣一種規定:


## 代碼驗證
package com.kobe.demo.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class TestForList {

    public static void main(String[] args) {

        List<String> list = new ArrayList<>();

        list.add("111");
        list.add("222");

        for (String item : list) {
            if ("222".equals(item)) {
                list.remove(item);
            }
        }

    }

}
Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
	at java.util.ArrayList$Itr.next(ArrayList.java:859)
	at com.kobe.demo.collection.TestForList.main(TestForList.java:15)

## 分析

​    因為foreach循環是Java提供的一種語法糖,所以我們用反編譯工具將以上代碼編譯后看看:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.kobe.demo.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class TestForList {
    public TestForList() {
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList();
        list.add("111");
        list.add("222");
        Iterator var2 = list.iterator();

        while(var2.hasNext()) {
            String item = (String)var2.next();
            if ("222".equals(item)) {
                list.remove(item);
            }
        }

    }
}

​    顯然,foreach循環實際上還是用Iterator迭代器while循環。根據堆棧信息,查看源碼,可以看到是當調用ArrayList里的內部類Itr的checkForComodification()方法報錯:

​    那我們看看modCount和expectModCount是什么?

  - modCount是ArrayList中的一個成員變量。它表示該集合實際被修改的次數
  - expectedModCount 是 ArrayList中的一個內部類Itr中的成員變量。expectedModCount表示這個迭代器期望該集合被修改的次數。其值是在ArrayList.iterator方法被調用的時候初始化的。只有通過迭代器對集合進行操作,該值才會改變。
  - Itr是一個Iterator的實現,使用ArrayList.iterator方法可以獲取到的迭代器就是Itr類的實例。

​    再看到remove方法的核心操作:

​    可以看到,它只修改了modCount,並沒有對expectedModCount做任何操作。


## 總結    foreach循環里,遍歷集合實際上是通過**迭代器Iterator**進行的,但是元素的remove/add方法卻是使用的是**集合自己**的方法,導致在遍歷的時候,會發現某個元素自己神不知鬼不覺地被刪除/增加了,這時候,就會拋出一個異常,告訴用戶可能會用多個線程對同一個集合發生了並發修改。


免責聲明!

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



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