天天看點

java.util.ConcurrentModificationException 異常解析

java.util.ConcurrentModificationException 異常解析

準備資料集合;

myList= new ArrayList<String>();

        myList.add("1");
        myList.add("2");
        myList.add("3");
        myList.add("4");
        myList.add("5");
        myList.add("6");      

隻要抛出出現異常,可以肯定的是代碼一定有錯誤的地方。先來看看都有哪些情況會出現ConcurrentModificationException異常,下面以ArrayList remove 操作進行舉例:

// 情況一
    public static  void fun1() {

        Iterator<String> it = myList.iterator();
        while(it.hasNext()){
            String value = it.next();
            if(value.equals("3")){
                myList.remove(value);
            }
        }
    }

    //情況二
    public static void fun2(){
        for (String value : myList) {
            if(value.equals("3")){
                myList.remove(value);
            }

        }
    }
 //情況三
    public static void fun2(){
     for (Iterator<string> it = myList.iterator(); it.hasNext();) {
     String value = it.next();
      if (value.equals( "3")) {
          myList.remove(value);  
     }
}
}      

以上三種情況都會出現異常:

異常資訊如下:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
    at java.util.AbstractList$Itr.next(Unknown Source)      

經過斷點調試,可以發現,異常都是出在, myList.remove(value); 這個語句之後,再次執行 while(it.hasNext()) 和 for (String value : myList)之後才會抛出的異常.

根本原因

以上都有3種出現異常的情況有一個共同的特點,都是使用Iterator進行周遊,且都是通過ArrayList.remove(Object) 進行删除操作。

想要找出根本原因,直接檢視ArrayList源碼看為什麼出現異常:

public class ArrayList<e> extends AbstractList<e>
        implements Cloneable, Serializable, RandomAccess


         @Override public boolean remove(Object object) {
        Object[] a = array;
        int s = size;
        if (object != null) {
            for (int i = 0; i < s; i++) {
                if (object.equals(a[i])) {
                    System.arraycopy(a, i + 1, a, i, --s - i);
                    a[s] = null;  // Prevent memory leak
                    size = s;
                    modCount++;  // 隻要删除成功都是累加
                    return true;
                }
            }
        } else {
            for (int i = 0; i < s; i++) {
                if (a[i] == null) {
                    System.arraycopy(a, i + 1, a, i, --s - i);
                    a[s] = null;  // Prevent memory leak
                    size = s;
                    modCount++;  // 隻要删除成功都是累加
                    return true;
                }
            }
        }
        return false;
    }   


    @Override public Iterator<e> iterator() {
        return new ArrayListIterator();
    }   

    private class ArrayListIterator implements Iterator<e> {
          ......

          // 全局修改總數儲存到目前類中
        /** The expected modCount value */
        private int expectedModCount = modCount;

        @SuppressWarnings("unchecked") public E next() {
            ArrayList<e> ourList = ArrayList.this;
            int rem = remaining;
               // 如果建立時的值不相同,抛出異常
            if (ourList.modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
            if (rem == 0) {
                throw new NoSuchElementException();
            }
            remaining = rem - 1;
            return      

List、Set、Map 都可以通過Iterator進行周遊,這裡僅僅是通過List舉例,在使用其他集合周遊時進行增删操作都需要留意是否會觸發ConcurrentModificationException異常。

解決方案(單線程下)

// 1 使用Iterator提供的remove方法,用于删除目前元素
 for (Iterator<string> it = myList.iterator(); it.hasNext();) {
     String value = it.next();
      if (value.equals( "3")) {
          it.remove();  // ok
     }
}
System. out.println( "List Value:" + myList.toString());

 // 2 建一個集合,記錄需要删除的元素,之後統一删除             
List<string> templist = new ArrayList<string>();
 for (String value : myList) {
      if (value.equals( "3")) {
          templist.remove(value);
     }
}
 // 可以檢視removeAll源碼,其中使用Iterator進行周遊
myList.removeAll(templist);
System. out.println( "List Value:" + myList.toString());        

  // 3. 使用線程安全CopyOnWriteArrayList進行删除操作
List<string> myList = new CopyOnWriteArrayList<string>();
myList.add( "1");
myList.add( "2");
myList.add( "3");
myList.add( "4");
myList.add( "5");

Iterator<string> it = myList.iterator();

 while (it.hasNext()) {
     String value = it.next();
      if (value.equals( "3")) {
          myList.remove( "4");
          myList.add( "6");
          myList.add( "7");
     }
}
System. out.println( "List Value:" + myList.toString());

 // 4. 不使用Iterator進行周遊,需要注意的是自己保證索引正常
 for ( int i = 0; i < myList.size(); i++) {
     String value = myList.get(i);
     System. out.println( "List Value:" + value);
      if (value.equals( "3")) {
          myList.remove(value);  // ok
          i--; // 因為位置發生改變,是以必須修改i的位置
     }
}
System. out.println( "List Value:"