java.util.ConcurrentModificationException 异常解析

来源:互联网 发布:数据价值网 邹志飞 编辑:程序博客网 时间:2024/06/10 22:34

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 (E) ourList.array[removalIndex = ourList.size - rem];        }        }

List、Set、Map 都可以通过Iterator进行遍历,这里仅仅是通过List举例,在使用其他集合遍历时进行增删操作都需要留意是否会触发ConcurrentModificationException异常。

具体分析见: http://www.cnblogs.com/andy-zhou/p/5339683.html

关键点就在于:调用list.remove()方法导致modCount和expectedModCount的值不一致。

解决方案(单线程下)

上面列举了会出现问题的几种情况,也分析了问题出现的根本原因,现在来总结一下怎样才是正确的,如果避免遍历时进行增删操作不会出现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:" + myList.toString());
阅读全文
1 0