在迭代和从ArrayList中删除元素时,如何避免java.util.ConcurrentModificationException

问题

我有一个我想迭代的ArrayList。迭代它时,我必须同时删除元素。显然这会引发ajava.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();


#1 热门回答(218 赞)

两种选择:

  • 创建一个要删除的值列表,在循环中添加到该列表,然后在末尾调用originalList.removeAll(valuesToRemove)
  • 在迭代器本身上使用remove()方法。请注意,这意味着你无法使用增强型for循环。

作为第二个选项的示例,从列表中删除长度大于5的任何字符串:

List<String> list = new ArrayList<String>();
...
for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
    String value = iterator.next();
    if (value.length() > 5) {
        iterator.remove();
    }
}

#2 热门回答(13 赞)

来自ArrayList的JavaDocs

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


#3 热门回答(7 赞)

你应该以传统方式迭代数组

每次从列表中删除元素时,后面的元素都将向前推进。只要你不更改迭代之外的元素,以下代码就可以工作。

public class Test(){
    private ArrayList<A> abc = new ArrayList<A>();

    public void doStuff(){
        for(int i = (abc.size() - 1); i >= 0; i--) 
            abc.get(i).doSomething();
    }

    public void removeA(A a){
        abc.remove(a);
    }
}