加载中…
个人资料
苍蝇也是肉呀
苍蝇也是肉呀
  • 博客等级:
  • 博客积分:0
  • 博客访问:1,480,897
  • 关注人气:209
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
相关博文
推荐博文
谁看过这篇博文
加载中…
正文 字体大小:

探究集合中(collection和map接口) java.util.ConcurrentModificationException

(2012-07-27 22:36:32)
标签:

java

集合collection

分类: JavaSE
首先应该明确,只要是实现了iterator接口都可以使用JDK1.5增加的新特性:增强的for循环。因此向数组、collection接口的实现类:ArrayList,LinkedList,hashset,treeset等 以及 map接口的hashMap,treeMap等都可以使用增强的for循环。
##通常我们使用迭代器都是读取其中的内容,而不是增加、删除、改变其中的内容(容易异常)

问题引入:经常在迭代集合(这里没有数组)元素时,会想对集合做修改(add/remove)操作(这里使用的add/remove方法是集合中的方法)这时就会抛出异常java.util.ConcurrentModificationException。
其实这一点在JDK的帮助文档的API(比如 ArrayList、HashSet、HashMap等集合的开始介绍中都有提到!!!!)中也有提到的!!!!
例如下面的代码:  //这里是显示使用迭代器
  for (Iterator<Integer> it = list.iterator(); it.hasNext();)
        {
            Integer val = it.next();
            if (val == 5)
            {
                list.remove(val);
            }
        }
或者 隐式地使用迭代器(增强的for循环)
    for(Integer i:list)
       {
          
if (i == 5)
            {
                list.remove(val);
            }

        }

问题探究:

      (以ArrayList来讲解)在ArrayList中,它的修改操作(add/remove)都会对modCount这个字段+1,modCount可 以看作一个版本号,每次集合中的元素被修改后,都会+1(即使溢出)。接下来再看看AbsrtactList中iteraor方法:
    public Iterator<E> iterator() {
    return new Itr();
     }
   它返回一个内部类,这个类实现了iterator接口,代码如下:
  private class Itr implements Iterator<E> {
    int cursor = 0;

    int lastRet = -1;

    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor != size();
    }

    public E next() {
        checkForComodification();
        try {
            E next = get(cursor);
            lastRet = cursor++;
            return next;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
    }

    public void remove() {
        if (lastRet == -1)
            throw new IllegalStateException();
        checkForComodification();

        try {
            AbstractList.this.remove(lastRet);
            if (lastRet < cursor)
                cursor--;
            lastRet = -1;
            // 修改expectedModCount 的值
            expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
            throw new ConcurrentModificationException();
        }
    }

    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
    }

在内部类Itr中,有一个字段expectedModCount ,初始化时等于modCount,即当我们调用list.iterator()返回迭代器时,该字段被初始化为等于modCount。在类Itr中 next/remove方法都有调用checkForComodification()方法,在该方法中检测modCount == expectedModCount,如果不相当则抛出异常ConcurrentModificationException。

前面说过,在集合的修改操作(add/remove)中,都对modCount进行了+1。
在看看刚开始提出的那段代码,在迭代过程中,执行list.remove(val),使得modCount+1,当下一次循环时,执行 it.next(),checkForComodification方法发现modCount != expectedModCount,则抛出异常。

总结产生原因:
    执行remove(Object o)方法之后,modCount和expectedModCount不相等了。然后当代码执行到next()方法时,判断了checkForComodification(),发现两个数值不等,就抛出了该Exception。从API中可以看到List等Collection的实现并没有同步化(即modCount和expectedModCount没有同步改变

解决办法:
   
如果想要在迭代的过程中,执行删除元素操作怎么办?
再来看看内部类Itr的remove()方法,在删除元素后,有这么一句expectedModCount = modCount,同步修改expectedModCount 的值。所以,如果需要在使用迭代器迭代时,删除元素,可以使用迭代器提供的remove方法。对于add操作,则在整个迭代器迭代过程中是不允许的。 其他集合(Map/Set)使用迭代器迭代也是一样。
改正后的代码如下:
 
    for (Iterator<Integer> it = list.iterator(); it.hasNext();)
        {
            Integer val = it.next();
            if (val == 5)
            {
                it.remove( );
            }
        }
但是不能这样改:(会又抛出java.lang.IllegalStateException异常)
      for(Integer i:list)
        {
            if(i == 5)
                list.iterator().remove();
        }


0

阅读 评论 收藏 转载 喜欢 打印举报/Report
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 电话:4000520066 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有