【问题标题】:ConcurrentModificationException when deleting an element from ArrayList从 ArrayList 中删除元素时出现 ConcurrentModificationException
【发布时间】:2014-06-03 22:26:01
【问题描述】:

当我运行以下代码时,Java 正在抛出 ConcurrentModificationException。知道这是为什么吗?

ArrayList<String> list1 = new ArrayList<String>();
list1.add("Hello");
list1.add("World");
list1.add("Good Evening");

for (String s : list1){
        list1.remove(2);
    System.out.println(s);
}

【问题讨论】:

标签: java collections


【解决方案1】:

如果您查看ConcurrentModificationException 的文档,您会发现

检测到并发的方法可能会抛出此异常 在不允许修改时修改对象。

例如一般不允许一个线程修改 一个集合,而另一个线程正在对其进行迭代

...

请注意,此异常并不总是表明对象具有 被不同的线程同时修改。如果单线程 发出一系列违反合同约定的方法调用 一个对象,该对象可能会抛出这个异常。 例如,如果一个 线程在迭代集合时直接修改集合 带有快速失败迭代器的集合,迭代器将抛出这个 例外

关于这个异常的重要一点是,我们不能保证它总是按照文档中的说明抛出

请注意,通常无法保证快速失败的行为 说,不可能在存在的情况下做出任何硬性保证 不同步的并发修改。 Fail-fast 操作抛出 ConcurrentModificationException 尽最大努力

同样来自ArrayList 文档

这个类的iteratorlistIterator返回的迭代器 方法是fail-fast:如果列表在结构上被修改 迭代器创建后的时间,除了通过 迭代器自己的删除或添加方法,迭代器会抛出一个 ConcurrentModificationException.

(强调我的)

因此,在通过增强的 for 循环对其进行迭代时,您无法操作 Collection 的内容(在您的情况下为 List),因为您不是通过迭代器进行操作的 for-each 在内部使用。

要解决它,只需获取您自己的Iterator 并在您的循环中使用它。要从集合中删除元素,请使用 remove,就像在这个例子中一样

Iterator<String> it = list1.iterator();
int i=0;
while(it.hasNext()){
    String s = it.next();
    i++;
    if (i==2){
        it.remove();
        System.out.println("removed: "+ s);
    }
}

【讨论】:

  • 查看所有项目的“已删除”输出。
  • Erick 也给出了类似的解释(虽然格式更短:) [stackoverflow.com/questions/9469210/…
  • 是的,现在我已经在 SO 上搜索了 ArrayListConcurrentModificationException,我发现很多问题的答案都很简短:)
【解决方案2】:

形成您可以阅读的doc

检测到并发的方法可能会抛出此异常 在不允许修改时修改对象。 比如一般不允许一个线程修改 一个集合,而另一个线程正在对其进行迭代。一般来说, 在这些情况下,迭代的结果是不确定的。 一些迭代器的实现(包括所有通用的 JRE 提供的目的集合实现)可以选择 如果检测到此行为,则抛出此异常。做的迭代器 这被称为快速失败迭代器,因为它们快速失败并且 干净利落,而不是冒着随意的、非确定性的行为的风险 未来的不确定时间。

进一步,假设您可以在每次迭代中删除第 2 项。您最终会遇到索引超出范围的异常:

  • 在第一次迭代中,您删除第 2 项 ("Good evening") 并将列表大小变为 1(第 0 项 "hello" 和第 1 项 "World"
  • 在下一次迭代中,您删除列表中实际上不存在的项目 #2。您的列表确实大小为 2,但计数器从 0 开始,因此您最终会删除不存在的内容:这是非确定性行为的一个示例。

【讨论】:

    【解决方案3】:

    在基础列表被修改后,你不能迭代一个列表。如果你这样做,它会给你ConcurrentModificationException

    要解决此问题,请使用java.util.ListIterator 进行列表迭代。

        ListIterator<String> it = list1.listIterator();
        for (String s : list1) {
            if (it.hasNext()) {
                String item = it.next();
                System.out.println(item);
            }
            }
    

    【讨论】:

      【解决方案4】:

      在迭代项目时,您不能使用 ArrayListremove() 方法删除项目。如果您还想在迭代时删除项目,请使用 Iterator

      但是您要根据索引删除一个项目,然后只需在循环外移到下一行即可解决您的问题。

       list1.remove(2);
      

      【讨论】:

      • 我同意,它适用于迭代器。但是,为什么当我在同一个循环中添加 if 语句时它不会给我一个错误。这是代码。 for (String s : list1){ if(s.equals("World")){ list1.remove(1); } }
      • 试试这个for (String s : list1){ if(s.equals("Hello")){ list1.remove(1); } }你不能删除一个尚未处理的项目,或者你可以说它的索引大于for-each循环的当前运行索引。
      • 我不确定,但这是个好问题。让我试着找出确切的原因。
      • @user3520698 正如我在回答中指出的那样,不能保证抛出此异常。快速失败操作会尽最大努力抛出 ConcurrentModificationException。造成这种情况的原因之一可能是线程缓存修改计数器(如果您查看ArrayList 类中的modCount,您将看不到可能阻止缓存此值的volatile 修饰符)。
      猜你喜欢
      • 1970-01-01
      • 2013-12-14
      • 2013-03-02
      • 2019-03-30
      • 1970-01-01
      • 1970-01-01
      • 2019-05-12
      • 1970-01-01
      • 2013-08-29
      相关资源
      最近更新 更多