【问题标题】:ConcurrentModificationException for ArrayList [duplicate]ArrayList 的 ConcurrentModificationException [重复]
【发布时间】:2011-03-12 05:21:51
【问题描述】:

我有以下代码:

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,任何人都可以解释它为什么会发生,即使代码在同一个线程中运行?我该如何避免呢?

【问题讨论】:

  • 对这个异常的解释是ArrayList的迭代器是一个fail-fast迭代器;即,当它检测到其集合同时已被修改时,它将失败(抛出异常)。与不抛出并发修改异常的故障安全迭代器相比(例如,在集合 ConcurrentHashMap 和 CopyOnWriteArrayList 上)

标签: java collections concurrency


【解决方案1】:

在遍历循环时,您试图在 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();
}

【讨论】:

    【解决方案2】:

    我们可以在迭代集合时使用并发集合类来避免 ConcurrentModificationException,例如 CopyOnWriteArrayList 而不是 ArrayList。

    查看这篇文章的 ConcurrentHashMap

    http://www.journaldev.com/122/hashmap-vs-concurrenthashmap-%E2%80%93-example-and-exploring-iterator

    【讨论】:

      【解决方案3】:

      我喜欢循环的相反顺序,例如:

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

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

      【讨论】:

        【解决方案4】:

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

        试试 java.util.concurrent.CopyOnWriteArrayList.class

        【讨论】:

        • 我对 HashMap 也有同样的问题,通过 Map 接口的另一个实现来解决。你应该自己测试一下。我不知道 CopyOnWriteArrayList 的详细信息
        【解决方案5】:

        如果您使用“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))
        • @HugoGresse 是的,但这是相反的方向。迭代器暴露了remove,这对于它的迭代来说是安全的,这是 foreach “丢失”的东西。
        【解决方案6】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-12-14
          • 2012-04-06
          • 1970-01-01
          • 2021-07-01
          • 1970-01-01
          • 2019-05-12
          • 2013-06-04
          相关资源
          最近更新 更多