为什么我在这个例子中没有得到java.util.ConcurrentModificationException?

时间:2011-11-18 21:35:13

标签: java list concurrentmodification foreach

注意:我知道Iterator#remove()方法。

在下面的代码示例中,我不明白为什么List.remove方法中的main会引发ConcurrentModificationException,而remove中的不是方法。

public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer toRemove) {
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer toRemove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }
}

10 个答案:

答案 0 :(得分:257)

原因如下: 正如在Javadoc中所说:

  

此类的迭代器和listIterator返回的迭代器   方法是快速失败的:如果列表在结构上被修改了   创建迭代器之后的时间,除了通过之外的任何方式   迭代器自己删除或添加方法,迭代器会抛出一个   ConcurrentModificationException的。

此检查在迭代器的next()方法中完成(如堆栈跟踪所示)。但是,只有当next()传递为真时,我们才会达到hasNext()方法,这是每个方法调用以检查是否满足边界。在你的remove方法中,当hasNext()检查是否需要返回另一个元素时,它会看到它返回了两个元素,现在删除一个元素后,列表只包含两个元素。所有都是桃子,我们完成了迭代。不会发生对并发修改的检查,因为这是在next()方法中完成的,该方法从未被调用过。

接下来我们进入第二个循环。删除第二个数字后,hasNext方法将再次检查是否可以返回更多值。它已经返回了两个值,但列表现在只包含一个。但这里的代码是:

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

1!= 2,所以我们继续使用next()方法,现在意识到某人一直在搞乱列表并触发异常。

希望能够解决你的问题。

摘要

List.remove()在从列表中删除第二个最后一个元素时不会抛出ConcurrentModificationException

答案 1 :(得分:42)

处理它的一种方法是从Collection(不是Collection本身)的副本中删除某些内容(如果适用)。 Clone原始收藏集通过Constructor制作副本。

  

检测到并发的方法可能抛出此异常   在不允许进行此类修改时修改对象。

对于您的具体情况,首先,我认为final是一种考虑您打算修改过去声明的列表的方法

private static final List<Integer> integerList;

另请考虑修改副本而不是原始列表。

List<Integer> copy = new ArrayList<Integer>(integerList);

for(Integer integer : integerList) {
    if(integer.equals(remove)) {                
        copy.remove(integer);
    }
}

答案 2 :(得分:13)

删除项目时,forward / iterator方法不起作用。您可以删除元素而不会出错,但是当您尝试访问已删除的项目时,您将收到运行时错误。你不能使用迭代器,因为当pushy显示它会导致ConcurrentModificationException,所以改为使用常规的for循环,但后退一步。

List<Integer> integerList;
integerList = new ArrayList<Integer>();
integerList.add(1);
integerList.add(2);
integerList.add(3);

int size= integerList.size();

//Item to remove
Integer remove = Integer.valueOf(3);

解决方案:

如果要删除列表元素,则以相反的顺序遍历数组。只需在列表中向后移动即可避免访问已删除的项目,从而删除例外。

//To remove items from the list, start from the end and go backwards through the arrayList
//This way if we remove one from the beginning as we go through, then we will avoid getting a runtime error
//for java.lang.IndexOutOfBoundsException or java.util.ConcurrentModificationException as when we used the iterator
for (int i=size-1; i> -1; i--) {
    if (integerList.get(i).equals(remove) ) {
        integerList.remove(i);
    }
}

答案 3 :(得分:6)

此代码段将始终抛出ConcurrentModificationException。

规则是“你不能修改(在列表中添加或删除元素),而不是使用迭代器迭代它(当你使用for-each循环时会发生这种情况)”。

<强>的JavaDoc:

此类的迭代器和listIterator方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,迭代器将抛出一个ConcurrentModificationException。

因此,如果您想要修改列表(或一般的任何集合),请使用迭代器,因为它会知道修改,因此这些修改将得到妥善处理。

希望这有帮助。

答案 4 :(得分:5)

我遇到了同样的问题,但是如果我将en元素添加到迭代列表中。 我是这样做的

public static void remove(Integer remove) {
    for(int i=0; i<integerList.size(); i++) {
        //here is maybe fine to deal with integerList.get(i)==null
        if(integerList.get(i).equals(remove)) {                
            integerList.remove(i);
        }
    }
}

现在一切都很顺利,因为你没有在你的列表上创建任何迭代器,你会手动迭代它#34;。条件i < integerList.size()永远不会欺骗你,因为当你删除/添加List列表减去/增量的List大小时...

希望它有所帮助,对我来说这是解决方案。

答案 5 :(得分:1)

这在Java 1.6

上运行良好

〜%javac RemoveListElementDemo.java
〜%java RemoveListElementDemo
〜%cat RemoveListElementDemo.java

import java.util.*;
public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer remove) {
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer remove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }
}

〜%

答案 6 :(得分:1)

如果使用copy-on-write集合,它将起作用;但是当你使用list.iterator()时,返回的迭代器将始终引用元素集合(如下所示) list.iterator()被调用,即使另一个线程修改了该集合。任何 基于copy-on-write的Iterator或ListIterator调用的变异方法 (例如添加,设置或删除)将抛出UnsupportedOperationException。

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new CopyOnWriteArrayList<>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer remove) {
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer remove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(remove)) {                
                integerList.remove(integer);
            }
        }
    }
}

答案 7 :(得分:1)

将迭代器for each更改为for loop以解决问题。

原因是:

  

此类的迭代器和listIterator返回的迭代器   方法是快速失败的:如果列表在结构上被修改了   创建迭代器之后的时间,除了通过之外的任何方式   迭代器自己删除或添加方法,迭代器会抛出一个   ConcurrentModificationException的。

- 推荐的Java文档。

答案 8 :(得分:0)

就我而言,我这样做了:

int cursor = 0;
do {
    if (integer.equals(remove))
        integerList.remove(cursor);
    else cursor++;
} while (cursor != integerList.size());

答案 9 :(得分:-1)

检查你的代码人......

在main方法中,您尝试删除不存在的第4个元素,从而删除错误。 在remove()方法中,您尝试删除那里的第3个元素,因此没有错误。