ArrayList的ConcurrentModificationException

93

我有以下代码:

private String toString(List<DrugStrength> aDrugStrengthList) {
    StringBuilder str = new StringBuilder();
        for (DrugStrength aDrugStrength : aDrugStrengthList) {
            if (!aDrugStrength.isValidDrugDescription()) {
                aDrugStrengthList.remove(aDrugStrength);
            }
        }
        str.append(aDrugStrengthList);
        if (str.indexOf("]") != -1) {
            str.insert(str.lastIndexOf("]"), "\n          " );
        }
    return str.toString();
}

当我尝试运行它时,我得到一个 ConcurrentModificationException, 有人可以解释一下为什么会出现这种情况,即使代码在同一个线程中运行?如何避免它?


1
这个异常的解释是ArrayList的迭代器是一种快速失败的迭代器;也就是说,当它检测到其集合在此期间已被修改时,它将会失败(抛出异常)。相比之下,不会抛出并发修改异常的安全失败迭代器(例如ConcurrentHashMap和CopyOnWriteArrayList集合) - Mike Argyriou
6个回答

180
你不能在使用“for each”循环浏览列表时从中删除元素。你可以使用Iterator。将以下代码替换:
for (DrugStrength aDrugStrength : aDrugStrengthList) {
    if (!aDrugStrength.isValidDrugDescription()) {
        aDrugStrengthList.remove(aDrugStrength);
    }
}

使用:

for (Iterator<DrugStrength> it = aDrugStrengthList.iterator(); it.hasNext(); ) {
    DrugStrength aDrugStrength = it.next();
    if (!aDrugStrength.isValidDrugDescription()) {
        it.remove();
    }
}

Java中的foreach语法实际上使用了Iterator,一些IDE会报告这个解决方案并建议用foreach (for(MyListener listener : MyListenerList))替换。 - Hugo Gresse
1
@HugoGresse 是的,但这是相反的方向。迭代器公开了remove,它对其迭代是安全的,而foreach则“失去”了这个功能。 - Konrad Garus

29

就像其他答案所说,您不能从正在迭代的集合中删除项目。您可以通过明确使用 Iterator 并在那里删除该项目来解决此问题。

Iterator<Item> iter = list.iterator();
while(iter.hasNext()) {
  Item blah = iter.next();
  if(...) {
    iter.remove(); // Removes the 'current' item
  }
}

23

我喜欢反向循环语句,像这样:

int size = list.size();
for (int i = size - 1; i >= 0; i--) {
    if(remove){
        list.remove(i);
    }
}

因为它不需要学习任何新的数据结构或类。


10

应该有一个支持此操作的List接口的并发实现。

尝试使用java.util.concurrent.CopyOnWriteArrayList.class。


我曾经遇到过HashMap的同样问题,但是通过另一种Map接口的实现进行了修复。你应该自己测试一下。关于CopyOnWriteArrayList的细节我不是很清楚。 - idiotgenius

8

在循环遍历过程中,您试图在remove()操作中更改List的值。这将导致ConcurrentModificationException异常。

请按照以下代码进行操作,它可以实现您想要的功能,同时不会抛出任何异常

private String toString(List aDrugStrengthList) {
        StringBuilder str = new StringBuilder();
    List removalList = new ArrayList();
    for (DrugStrength aDrugStrength : aDrugStrengthList) {
        if (!aDrugStrength.isValidDrugDescription()) {
            removalList.add(aDrugStrength);
        }
    }
    aDrugStrengthList.removeAll(removalList);
    str.append(aDrugStrengthList);
    if (str.indexOf("]") != -1) {
        str.insert(str.lastIndexOf("]"), "\n          " );
    }
    return str.toString();
}

4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接