集合遍历时删除元素异常(ConcurrentModificationException)分析

时间:2022-09-03 16:26:34
  • 传统方式下的Collection在迭代集合时,不允许对集合进行修改。
  • 根据AbstractListcheckForComodification方法的源码,分析产生ConcurrentModificationException异常的原因


传统方式下的Collection在迭代集合时,不允许对集合进行修改。
根据AbstractList的checkForComodification方法的源码,分析产生ConcurrentModificationException异常的原因


情况1:删除倒数一个元素
ArrayList<String> list = new ArrayList<String>();
list.add("张三");
list.add("李四");
list.add("王五");


// 迭代的时候删除数据--看是否报异常
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
   String name = iterator.next();
   if (name.equals("李四")) {
       list.remove(name);
   }


   System.out.println(name);
}
输出:
张三
李四


情况2:删除开头或中间元素
  // 添加数据--三个
        ArrayList<String> list = new ArrayList<String>();
        list.add("张三");
        list.add("李四");
        list.add("王五");


        // 迭代的时候删除数据--看是否报异常
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String name = iterator.next();
            if (name.equals("张三")) {
                list.remove(name);// 注意:这里调用的时候List的remove方法,而不是Iterator的方法。 删除“张三”或“王五”均会出现异常
            }


            System.out.println(name);
        }


张三


java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at com.edu.fzu.googleplay.ExampleUnitTest.addition_isCorrect(ExampleUnitTest.java:23)






问1:为何情况1和情况2出现不同情况:---分析源代码,重点分析整个调用过程中调用的函数(hasNext和remove)
// Itr源代码
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;


       public boolean hasNext() {
           return cursor != size; // size为集合中元素的个数
       }


       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.remove方法
       public void remove() {
           checkForComodification();


           try {
               ArrayList.this.remove(lastRet);// size字段减1
               cursor = lastRet;
               lastRet = -1;
               expectedModCount = modCount;
           } catch (IndexOutOfBoundsException ex) {
               throw new ConcurrentModificationException();
           }
       }
       */


       final void checkForComodification() {// 检查修改和当前版本号是否一致,不一致则抛出异常
           if (modCount != expectedModCount)
               throw new ConcurrentModificationException();
       }


    }


    // List.remove
    @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++;// 核心代码:修改了版本号。这样当checkForComodification的时候,modCount值就和expectedModCount不同
                   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;
   }


    答1:
    出现情况1原因:当name为“李四”的时候,执行remove方法,此时size减1(变为2),此时再次执行hasNext的时候cursor为2,size也为2
    所以就打印两次:张三,李四。然后退出,没报异常
    出现情况2原因:
    当调用remove方法的时候修改了modCount值(参考源代码),这样导致expectedModCount和modCount不同,这样下次checkForComodification就抛出异常