List remove ConcurrentModificationException源碼分析


代碼塊
Java
 
 
 
 
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.mashibing.tank.Test2.main(Test2.java:14)
 

ArrayList在迭代刪除元素時,如果使用不當會發生concurrentModification 異常,常見的錯誤使用場景

 

代碼塊
Java
 
 
 
 
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
    Integer next = iterator.next();
        list.remove(next);
}
 
代碼塊
Java
 
 
 
 
for (Integer next:list
     ) {
    list.remove(next);
}
 

發生異常原因:

首先明確以上兩種方式是等效的,原因:增強for在反編譯後語句

代碼塊
Java
 
 
 
 
Iterator var4 = var1.iterator();
while(var4.hasNext()) {
    Integer var3 = (Integer)var4.next();
    var1.remove(var3);
}
 

 

然後我們去分析發生concurrentModification原因:

由於異常發生在 :java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909),我們先去看下此處代碼

發現remove(intxOrObject) 方法爲 ArrayList 內部類Itr的一個方法 ,當modCount != expectedModCount 時會拋出異常ConcurrentModificationException。

先來解釋下概念:

modCount:爲 成員變量 list 被修改的次數 ,每次add ,remove都會+1 (The number of times this list has been )

代碼塊
Java
 
 
 
 
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
      //The number of times this list has been
      protected transient int modCount = 0;
 

expectedModCount:爲listIterator 方法 局部變量,表示對ArrayList修改次數的期望值,它的初始值爲modCount

 

代碼塊
Java
 
 
 
 
 
public Iterator<E> iterator() {
    return listIterator();
}
public ListIterator<E> listIterator(final int index) {
    checkForComodification();
    rangeCheckForAdd(index);
    final int offset = this.offset;
    return new ListIterator<E>() {
        int cursor = index;
        int lastRet = -1;
        //注意這裏
        int expectedModCount = ArrayList.this.modCount;
        .......
        }
        ....
      }
      .....
  }
 
代碼塊
Java
 
 
 
 
 
private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;
    Itr() {}
    public boolean hasNext() {
        return cursor != size;
    }
    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }
}
 

這裏假設原始list 長度爲10(只做過新增,沒有做過刪除操作) 。

這時候最初 AbstractList.modCount =10 ;

Iterator.expectedModCount = modCount =10 ;

1.當第一次循環調用next時,無變動list ,所以expectedModCount = modCount =10 ,

2.當 arrayList.remove(indexOrObject)時,modCount++ 變爲11 ,expectedModCount 依然爲10 。

3.第二次調用next 時,Itr 會執行checkForComodification() ,也就是判斷expectedModCount、 modCount 是否一致,如果不一致則拋出ConcurrentModificationException 。

正確姿勢:

關注點:

1.增強for循環不要刪除元素

2.使用Iterator<Integer> iterator = list.iterator(); 方式時,先獲取元素,然後使用iterator.remove() 進行刪除

代碼塊
Java
 
 
 
 
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext() ) {
    System.out.println(iterator.next());
    iterator.remove();
}
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
    list.remove(i);
}
 

爲什麼Iterator<Integer> iterator = list.iterator(); 方式時 用iterator remove 不會出現問題?看源碼:

代碼塊
Java
 
 
 
 
private class Itr implements Iterator<E> {
    public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();
    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        //關注這裏, Itr裏將modCount 賦值給expectedModCount 
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
    }
 

爲什麼普通循環不會有ConcurrentModificationException ,看源碼:

代碼塊
Java
 
 
 
 
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
    private void fastRemove(int index) {
        modCount++;
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    }
}
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章