为啥在此示例中我没有收到 java.util.ConcurrentModificationException?
Posted
技术标签:
【中文标题】为啥在此示例中我没有收到 java.util.ConcurrentModificationException?【英文标题】:Why am I not getting a java.util.ConcurrentModificationException in this example?为什么在此示例中我没有收到 java.util.ConcurrentModificationException? 【发布时间】:2012-01-01 15:11:24 【问题描述】:注意:我知道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);
【问题讨论】:
在迭代列表时从列表中删除元素的唯一安全方法是使用Iterator#remove()
。你为什么要这样做?
@MattBall:我只是想看看这里可能是什么原因。因为,这两种方法中的“增强型 for 循环”是相同的,但一种抛出 ConcurrentModificationException
而另一种则没有。
您删除的元素有所不同。在该方法中,您删除了“中间元素”。主要是删除最后一个。如果您交换数字,您的方法中会出现异常。仍然不确定为什么会这样。
我遇到了类似的问题,当我的循环迭代了一个在我删除循环中的项目后不存在的位置时。我只是通过在循环中添加return;
来解决这个问题。
在 java8 android 上,删除除最后一个以外的元素会调用 ConcurrentModificationException。所以对于你的情况,删除函数会得到一个与你之前观察到的相反的异常。
【参考方案1】:
原因如下: 正如 Javadoc 中所说:
这个类的迭代器和listIterator返回的迭代器 方法是快速失败的:如果列表在任何结构上被修改 迭代器创建后的时间,除了通过 迭代器自己的删除或添加方法,迭代器会抛出一个 ConcurrentModificationException。
这项检查是在迭代器的next()
方法中完成的(正如您在堆栈跟踪中看到的那样)。但是只有当hasNext()
传递为真时,我们才会到达next()
方法,这是由 for each 调用以检查是否满足边界的方法。在你的 remove 方法中,当hasNext()
检查它是否需要返回另一个元素时,它会看到它返回了两个元素,现在删除一个元素后,列表只包含两个元素。所以一切都很顺利,我们完成了迭代。不会发生并发修改检查,因为这是在从未调用过的 next()
方法中完成的。
接下来我们进入第二个循环。在我们删除第二个数字后,hasNext 方法将再次检查是否可以返回更多值。它已经返回了两个值,但列表现在只包含一个。但是这里的代码是:
public boolean hasNext()
return cursor != size();
1 != 2,所以我们继续使用 next()
方法,它现在意识到有人在弄乱列表并触发异常。
希望能解决您的问题。
总结
List.remove()
从列表中删除倒数第二个元素时不会抛出 ConcurrentModificationException
。
【讨论】:
@pushy:只回答似乎回答了问题的实际问题,并且解释得很好。我接受这个答案,也+1。谢谢。【参考方案2】:处理它的一种方法是从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);
【讨论】:
【参考方案3】:前向/迭代器方法在删除项目时不起作用。您可以删除元素而不会出错,但是当您尝试访问已删除的项目时会出现运行时错误。您不能使用迭代器,因为 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);
【讨论】:
好主意!【参考方案4】:这个 sn-p 将总是抛出 ConcurrentModificationException。
规则是“在使用迭代器迭代它时,你不能修改(从列表中添加或删除元素)(当你使用 for-each 循环时会发生这种情况)”。
JavaDocs:
这个类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:如果在迭代器创建后的任何时候列表在结构上被修改,除了通过迭代器自己的 remove 或 add 方法之外的任何方式,迭代器将抛出 ConcurrentModificationException。
因此,如果您想修改列表(或一般的任何集合),请使用迭代器,因为它会知道修改,因此将正确处理这些修改。
希望这会有所帮助。
【讨论】:
OP 明确指出其中一个循环不会引发异常,而疑问是发生这种情况的原因。 你所说的“疑问”是什么意思?【参考方案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);
现在一切正常,因为您没有在列表上创建任何迭代器,而是“手动”迭代它。并且条件i < integerList.size()
永远不会欺骗您,因为当您将某些内容删除/添加到列表减量/增量的列表大小时..
希望对我有帮助,这就是解决方案。
【讨论】:
这不是真的!证据:运行此 sn-p 以查看结果: public static void main(String... args) List如果您使用写时复制集合,它将起作用;但是,当您使用 list.iterator() 时,返回的 Iterator 将始终引用元素的集合,就像它在(如下所示)时一样 list.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】:这在 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);
~%
【讨论】:
抱歉打错了这在 Java 1.6 上运行良好 嗯...可能你有不同的实现。但根据规范,它应该这样做,IMO。看看@Pushy 的回答。 不幸的是,id 在 java 1.8 上没有【参考方案8】:就我而言,我是这样做的:
int cursor = 0;
do
if (integer.equals(remove))
integerList.remove(cursor);
else cursor++;
while (cursor != integerList.size());
【讨论】:
【参考方案9】:将迭代器for each
改为for loop
即可解决。
原因是:
这个类的迭代器和listIterator返回的迭代器 方法是快速失败的:如果列表在任何结构上被修改 迭代器创建后的时间,除了通过 迭代器自己的删除或添加方法,迭代器会抛出一个 ConcurrentModificationException。
--参考的 Java 文档。
【讨论】:
【参考方案10】:检查你的代码人......
在 main 方法中,您试图删除不存在的第 4 个元素,因此会出现错误。 在 remove() 方法中,您尝试删除存在的第三个元素,因此没有错误。
【讨论】:
你错了:数字2
和3
不是列表的索引,而是元素。两个删除逻辑都针对列表元素检查equals
,而不是元素的索引。此外,如果它与索引相关,它将是IndexOutOfBoundsException
,而不是ConcurrentModificationException
。以上是关于为啥在此示例中我没有收到 java.util.ConcurrentModificationException?的主要内容,如果未能解决你的问题,请参考以下文章
为啥在这个 angular2 示例中我仍然需要 @inject?
为啥我在此 SQL Server 内联表值函数上收到 11555 错误?
为啥我会收到“错误:‘pthread_delay_np’未在此范围内声明”?