牛骨文教育服务平台(让学习变的简单)
博文笔记

java foreach不适用的一种情况

创建时间:2009-05-07 投稿人: 浏览次数:7866
今天遇到java.util.ConcurrentModificationException,从网上找到如下文章
java.util.ConcurrentModificationException

遍历集合找到特定的元素并将其删除,两种实现:

    private void testDelete() {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10; i++) {
            String str = "ck0" + i;
            list.add(str);
        }

        for (Iterator it = list.iterator(); it.hasNext();) {
            String str = (String) it.next();
            if (str.equals("ck05")) {
                // list.remove(str);  // 第一种删除方法
                it.remove();  // 第二种删除方法
            }
        }
    }

当通过list.remove(str)删除时报异常:java.util.ConcurrentModificationException。而通过it.remove()删除时一切正常。

先看看List中的remove方法:

这里用的ArrayList,ArrayList中remove方法源代码:

    public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }

    private void fastRemove(int index) {
        modCount++; // 特别注意这里,这里只增加了modCount的值
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index + 1, elementData, index,
                    numMoved);
        elementData[--size] = null; // Let gc do its work
    }

到这里似乎还没有找到抛出异常的地方,接着看。删除后得到下一个元素的代码,it.next():  it为AbstractList的内部类Iterator的一个实例。

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

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

也就是集合被修改的次数(modCount)和它的期望值(expectedModCount)不同,那么就会抛出ConcurrentModificationException异常。

再来看看Iterator的remove()方法的源代码:

    public void remove() {
        if (lastRet == -1)
            throw new IllegalStateException();
        checkForComodification();
        try {
            AbstractList.this.remove(lastRet);
            if (lastRet < cursor)
                cursor--;
            lastRet = -1;
            expectedModCount = modCount; // 设置expectedModCount
        } catch (IndexOutOfBoundsException e) {
            throw new ConcurrentModificationException();
        }
    }

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

到这里问题就很明白了!

在我们foreach时也会出现这种情况,但是在用普通的for循环却不会。

    for(String str : list){
        if(str.equals("ck05")){
            list.remove(str);  // 报异常
        }
    }


    for(int i = 0; i < list.size(); i++){
        String str = list.get(i);
        if(str.equals("ck05")){
            list.remove(str); // 正常
        }
    }

“之所以可以这样做(用foreach遍历集合),是因为Java SE5引入了新的的被称为Iterable的接口,该接口保护了一个能够产生Iterator的iterator()方法,并且Iterable接口被 foreach用来在序列中移动。”(<<Thinking in java>>)

网上其他解释:

http://www.javaeye.com/topic/124788    

  文中指出:“有意思的是如果你的 Collection / Map 对象实际只有一个元素的时候, ConcurrentModificationException 异常并不会被抛出。这也就是为什么在 javadoc 里面指出: it would be wrong to write a program that depended on this exception for its correctness: ConcurrentModificationException should be used only to detect bugs.” 测试了下,当只要一个元素时仍然会报异常!

http://www.javaeye.com/topic/145383  同意这种解释,从代码出发,比较好理解。 

http://gceclub.sun.com.cn/yuanchuang/week-14/iterator.html

 

 

 

嗯,是否有还使用foreach的解决方案?

声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。