Java 迭代器刪除元素ConcurrentModificationException異常。


Java是不支持容器類在使用迭代器迭代過程中,使用如 list.remove(obj)方法刪除元素。否則會拋出ava.util.ConcurrentModificationException異常。應該使用iterator.remove()方法刪除當前迭代到的元素。

這是因為Java集合中有一種叫fail-fast的機制,即如果多個線程對同一個集合的內容進行操作時,則會產生fail-fast事件,即拋出異常。

比如下面代碼

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

public class Hello {

    public static void main(String[] args) {
        List<String> all=new ArrayList<String>();
        all.add("Hello");
        all.add("_");
        all.add("World!!");
        Iterator<String> iterator=all.iterator();
        while(iterator.hasNext())
        {
            String str=iterator.next();
            if("Hello".equals(str))
            {
                all.remove(str);

            }
            else
            {
                System.out.println( str+" ");
            }
        }
        System.out.println("\n刪除\"_\"之后的集合當中的數據為:"+all);
    }
}

刪除元素會拋出下面異常

這里在迭代過程中改變了原來集合的存儲內容,因此出發了fail-fast機制。

fail-fast機制是這樣設置的。以上面代碼拋出的異常為例。查看ArrayList.類的源碼,可以看到拋出異常的函數為

這里的modCount文檔是這樣解釋的

The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results.

大概意思就是list的結構被修改的次數。結構的修改包括改變list的大小,或者其他可能使迭代結果產生錯誤的干擾行為。

expectedModCount注釋為:

The modCount value that the iterator believes that the backing List should have.  If this expectation is violated, the iterator has detected concurrent modification.

因此若是在迭代過程中改變了List的結構,即使用了remove,或者等add方法則會導致expectedModCount的值與modCount的值不同,就會拋出異常。

但是有一個奇特的現象,如果我們使用List.remove(obj)方法刪除倒數第二個元素則不會超出異常。如

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

public class Hello {

    public static void main(String[] args) {
        List<String> all=new ArrayList<String>();
        all.add("Hello");
        all.add("_");
        all.add("World!!");
        Iterator<String> iterator=all.iterator();
        while(iterator.hasNext())
        {
            String str=iterator.next();
            if("_".equals(str))
            {
                all.remove(str);

            }
            else
            {
                System.out.println( str+" ");
            }
        }
        System.out.println("\n刪除\"_\"之后的集合當中的數據為:"+all);
    }
}

注意這里刪除的'_'為集合中的倒數第二個元素。同時注意代碼有,若沒有刪除此元素,則輸出此元素,即else里面的內容。結果為

不是說,不能用List.remove(obj)的方式,只能用iterator.remove()的方式刪除,這里為什么沒有拋異常。原因是這樣。

看ArrayList的hasNext()方法與next()方法的源碼

size為當前集合的大小,cursor為下個元素的索引,初始化為0

其中checkForComodification的源碼為,兩個變量的的解釋上面也已經說了。

我們的迭代器部分代碼按下面步驟執行:

all里面存儲了“Hello”,"_","world!"三個元素

  1. iterator.hasNext(),
    1. cursor=0,size=3。cursor!=size,返回true,進入while內
  2. String str=iterator.next(),
    1. checkForComodification(),此時modCount=0,expectedModCount=0,因此沒有拋出異常,
    2. cursor=i+1,即cursor=1
    3. return elementData[lastRet=i],str='Hello'
  3. '_'.equals(str)為False,輸出Hello
  4. iterator.hasNext(),
    1. cursor=1,size=3。cursor!=size,返回true,進入while內
  5. String str=iterator.next(),
    1. checkForComodification(),此時modCount=0,expectedModCount=0,因此沒有拋出異常,
    2. cursor=i+1,即cursor=2
    3. return elementData[lastRet=i],str='_'
  6. '_'.equals(str)為Ture,
    1. all.remove(str)
    2. modCount=1,因為改變了List的結構,因此modCount+1。
  7. iterator.hasNext(),
    1. cursor=2,size=2。cursor!=size,返回False,退出

可以看到,在這里就已經推出while循環了。不會在執行checkForComodification()方法,判斷List結構有沒有改變。

結論:因此只要對倒數第二個元素執行remove()方法,雖然會改變集合結構,但是因為也同時改變了集合size,使size與cursor的大小一樣,就會直接循環,不會報異常。


免責聲明!

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



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