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

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

在下面的代码示例中,我不明白为什么 main方法中的 List.remove抛出 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);
}
}
}
}
269508 次浏览

这个代码片段将 一直都是抛出 ConcurrentModficationException。

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

JavaDocs:

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

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

希望这个能帮上忙。

一种处理它的方法是从 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);
}
}

原因如下: 正如 Javadoc 所言:

此类的迭代器和 listIterator 返回的迭代器 方法是快速失败的: 如果列表在任何 除了通过 迭代器自己的移除或添加方法时,迭代器将抛出一个 异常。

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

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

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

一!= 2,因此我们继续使用 next()方法,该方法现在意识到有人对列表进行了干扰,并触发异常。

希望这样你的问题就清楚了。

摘要

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

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

~ %

检查你的密码。

在 main 方法中,您试图删除不存在的第4个元素,因此出现了错误。 在 move ()方法中,您试图删除第3个元素,因此没有错误。

转发/迭代器方法在删除项目时不起作用。您可以删除元素而不会出现错误,但是当您尝试访问已删除的项时,会得到一个运行时错误。您不能使用迭代器,因为正如极力展示的那样,它会导致 ConcurrentModficationException 异常,所以应该使用常规 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);
}
}

我也有同样的问题,但是如果我在迭代列表中添加 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()永远不会欺骗你,因为当你删除/添加一些列表大小的减量/增量。.

希望能有所帮助,对我来说,这就是解决办法。

对我来说,我是这样做的:

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

如果您使用写时复制集合,那么它将工作; 但是当您使用 list.Iterator ()时,返回的 Iterator 将始终引用元素集合,就像以前一样(如下所示) 即使另一个线程修改了集合,也会调用 list.iterator () 对基于写上复制的迭代器或 ListIterator 调用的变异方法 (如 add、 set 或 delete)将引发 Unsupport tedOperationException。

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

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

原因是:

此类的迭代器和 listIterator 返回的迭代器 方法是快速失败的: 如果列表在任何 除了通过 迭代器自己的移除或添加方法时,迭代器将抛出一个 异常。

--参考 Java 文档。