为什么在此示例中我没有得到 java.util.ConcurrentModificationException?

2022-08-31 06:42:31

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

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

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);
            }
        }
    }
}

答案 1

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

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

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

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

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

1 != 2,因此我们继续使用该方法,该方法现在意识到有人一直在弄乱列表并触发异常。next()

希望这能解决您的问题。

总结

List.remove()当它从列表中删除倒数第二个元素时,不会抛出。ConcurrentModificationException


答案 2

处理它的一种方法是从副本(不是集合本身)中删除某些内容(如果适用)。 原始集合它通过.CollectionCloneConstructor

当不允许对对象进行并发修改时,检测到对象并发修改的方法可能会引发此异常。

对于您的具体情况,首先,我认为考虑到您打算修改列表过去的声明,我不认为这是一种方法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);
    }
}