181

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

在下面的代码示例中,我不明白为什么List.removeinmain方法会 throws ConcurrentModificationException,但不是inremove方法。

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

10 回答 10

266

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

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

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

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

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

1 != 2,所以我们继续这个next()方法,它现在意识到有人一直在弄乱列表并触发异常。

希望能解决你的问题。

概括

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

于 2011-11-18T22:11:22.387 回答
42

处理它的一种方法是从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);
    }
}
于 2011-11-18T21:45:16.750 回答
15

删除项目时,前向/迭代器方法不起作用。您可以删除元素而不会出错,但是当您尝试访问已删除的项目时会出现运行时错误。您不能使用迭代器,因为 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);
    }
}
于 2013-09-11T12:15:42.593 回答
8

此代码段将始终抛出 ConcurrentModificationException。

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

JavaDocs:

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

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

希望这可以帮助。

于 2011-11-18T21:41:49.543 回答
4

我遇到了同样的问题,但如果我将 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);
        }
    }
}

现在一切正常,因为您没有在列表上创建任何迭代器,而是“手动”迭代它。并且条件i < integerList.size()永远不会欺骗您,因为当您将某些内容删除/添加到列表减量/增量的列表大小时..

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

于 2014-05-11T12:19:52.450 回答
1

如果您使用写时复制集合,它将起作用;但是,当您使用 list.iterator() 时,即使另一个线程修改了集合,返回的 Iterator 将始终引用调用(如下) list.iterator() 时的元素集合。在基于写入时复制的 Iterator 或 ListIterator 上调用的任何变异方法(例如 add、set 或 remove)都将引发 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);
            }
        }
    }
}
于 2015-02-24T20:05:12.723 回答
0

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

~ %

于 2012-07-12T13:26:35.557 回答
0

就我而言,我是这样做的:

int cursor = 0;
do {
    if (integer.equals(remove))
        integerList.remove(cursor);
    else cursor++;
} while (cursor != integerList.size());
于 2014-11-05T12:29:18.737 回答
0

将Iterator 更改for eachfor loop来解决。

原因是:

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

--引用的 Java 文档。

于 2018-05-07T10:25:00.363 回答
-2

检查你的代码人....

在主要方法中,您试图删除不存在的第 4 个元素,因此会出现错误。在 remove() 方法中,您尝试删除存在的第三个元素,因此没有错误。

于 2013-08-22T12:47:01.760 回答