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

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/lin111000713/article/details/52694660
  • 传统方式下的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就抛出异常














展开阅读全文

没有更多推荐了,返回首页