我有一个我想迭代的ArrayList . 迭代它时,我必须同时删除元素 . 显然这会引发 java.util.ConcurrentModificationException
.
处理此问题的最佳做法是什么?我应该先克隆列表吗?
我删除不在循环本身但是代码的另一部分的元素 .
我的代码看起来像这样:
public class Test() {
private ArrayList<A> abc = new ArrayList<A>();
public void doStuff() {
for (A a : abc)
a.doSomething();
}
public void removeA(A a) {
abc.remove(a);
}
}
a.doSomething
可能会调用 Test.removeA()
;
17 回答
来自ArrayList的JavaDocs
你应该以传统的方式迭代数组
每次从列表中删除元素时,后面的元素都将向前推进 . 只要您不更改迭代之外的元素,以下代码就可以工作 .
以正常方式执行循环,
java.util.ConcurrentModificationException
是与访问的元素相关的错误 .所以尝试:
两种选择:
创建一个要删除的值列表,在循环中添加到该列表,然后在末尾调用
originalList.removeAll(valuesToRemove)
在迭代器本身上使用
remove()
方法 . 请注意,这意味着您无法使用增强型for循环 .作为第二个选项的示例,从列表中删除长度大于5的任何字符串:
您试图从高级“for循环”中的列表中删除值,这是不可能的,即使您应用任何技巧(您在代码中执行) . 更好的方法是将迭代器级别编码为其他建议 .
我想知道人们怎么没有建议传统的for循环方法 .
这也有效 .
一种选择是将
removeA
方法修改为 -但这意味着你的
doSomething()
应该能够将iterator
传递给remove
方法 . 不是一个好主意 .你能用两步法做到这一点:在迭代列表的第一个循环中,不是删除所选元素,而是将它们标记为要删除 . 为此,您可以简单地将这些元素(浅拷贝)复制到另一个
List
中 .然后,一旦完成迭代,只需从第一个列表中执行
removeAll
第二个列表中的所有元素 .这是一个示例,我使用不同的列表添加要删除的对象,然后我使用stream.foreach从原始列表中删除元素:
在Java 8中,您可以使用Collection Interface并通过调用removeIf方法执行此操作:
更多信息可以在here找到
像这样做简单:
而不是使用For循环,使用normal for循环 . 例如,下面的代码删除了数组列表中的所有元素,而没有给出java.util.ConcurrentModificationException . 您可以根据用例修改循环中的条件 .
“我应该首先克隆列表吗?”
这将是最简单的解决方案,从克隆中删除,并在删除后复制克隆 .
我的rummikub游戏的一个例子:
使用流的替代Java 8解决方案:
在Java 7中,您可以使用Guava代替:
请注意,Guava示例会生成一个不可变列表,该列表可能是您想要的,也可能不是 .
在迭代列表时,如果要删除元素是可能的 . 让我看看下面的例子,
我有上面的数组列表名称 . 我想从上面的列表中删除“def”名称,
上面的代码抛出 ConcurrentModificationException 异常,因为您在迭代时修改列表 .
所以,通过这种方式从Arraylist中删除“def”名称,
上面的代码,通过迭代器我们可以从Arraylist中删除“def”名称并尝试打印数组,你会看到下面的输出 .
输出:[abc,ghi,xyz]
如果您的目标是从列表中删除所有元素,则可以迭代每个项目,然后调用:
我迟到了,但我回答这个问题,因为我认为这个解决方案简单而优雅:
这一切都是为了从一个列表更新到另一个列表,你可以做所有从一个列表和方法更新您检查列表和橡皮擦或添加列表之间的元素 . 这意味着两个列表总是大小相同
使用Iterator而不是Array List
将一个集合转换为类型匹配的迭代器
然后移动到下一个元素并删除
移到下一个是很重要的,因为它应该采用索引删除元素 .
只需在ArrayList.remove(A)语句后添加一个中断