1. 程式人生 > >為什麼在foreach迴圈中進行元素remove/add操作,會拋ConcurrentModificationException 異常?

為什麼在foreach迴圈中進行元素remove/add操作,會拋ConcurrentModificationException 異常?

執行以下程式碼:

 @Test
    public void test() {
        List<String> list = new ArrayList<>();
        list.add("A");
        list.add("B");
        for (String temp : list) {
            // 當為A時執行正常,改成B時異常ConcurrentModificationException
            if ("B".equals(temp)) {
                list.remove(temp);
            }
        }
    }

執行結果:

編譯後的Class檔案:

 @Test
    public void test() {
        List<String> list = new ArrayList();
        list.add("A");
        list.add("B");
        Iterator var2 = list.iterator();

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

    }

可以發現原始碼與編譯後的位元組碼不一樣,原始碼中的“for (String temp : list) {”在真實的位元組碼中是

“Iterator var2 = list.iterator();
while(var2.hasNext()) {
    String temp = (String)var2.next();”

對應的原始碼:

list.iterator()對應方法的原始碼:

new Itr()建立物件時,會將modCount值賦值給expectedModCount,而modCount該欄位在ArrayList中標識當前物件的修改次數(包括remove和add方法)

add()

remove()

進入while迴圈時,執行var2.hasNext()方法:

cursor初始化的時候是0,每執行一次next()方法,值自動加1,cursor == size時會跳出迴圈

當執行next()方法時,首先會執行checkForComodification()方法

檢視程式碼,可以看出當modCount != expectedModCount時,就會丟擲ConcurrentModificationException

expectedModCount的值是在建立Itr物件賦值的,而遍歷集合的時候,再執行一次remove或add就會導致modCount的值修改,而此時expectedModCount的值未變化,就會報異常ConcurrentModificationException。

如果非要在迴圈裡面remove、add 元素,請使用 Iterator 方式,如果併發操作,需要對 Iterator 物件加鎖。

  ListIterator iterator = list.listIterator();
        while (iterator.hasNext()) {
            String item = (String) iterator.next();
            if("B".equals(item)){
                iterator.add("C");
//                iterator.remove();
            }
            System.out.println("改變後的資料:" + list.toString());
        }