【问题标题】:Remove two objects at the same time from an ArrayList without causing a ConcurrentModificationException?同时从 ArrayList 中删除两个对象而不会导致 ConcurrentModificationException?
【发布时间】:2014-12-06 18:23:31
【问题描述】:

我有以下代码引发 ConcurrentModificationException。 有人能解释一下为什么会这样吗?

public void foo(ArrayList<Bet> bets)
   Iterator it1 = bets.iterator();
   while(it1.hasNext())
      Bet b1 = (Bet) bets.next()
      Iterator it2 = bets.iterator();
      while(it2.hasNext())
         if(bet1.equals(bet2))
             it2.remove();
             it1.remove();   //ConcurrentModificationException thrown here

是不是每次 iterator.next() 调用我只能调用一次 iterator.remove(), 并且在下一次调用 iterator.next() 之前调用 remove 两次会导致这种情况?

任何帮助将非常感谢。

【问题讨论】:

    标签: java iterator iteration concurrentmodification


    【解决方案1】:

    您需要将所有删除内容收集到 Set 中,并在所有迭代完成后删除它们。

    public void foo(ArrayList<Bet> bets) {
        Set<Bet> remove = new HashSet<Bet>();
        for ( Bet bet1 : bets ) {
            for ( Bet bet2 : bets ) {
                // Not the same but equal.
                if ( bet1 != bet2 && bet1.equals(bet2)) {
                    remove.add(bet1);
                    remove.add(bet2);
                }
            }
        }
        bets.removeAll(remove);
    }
    

    【讨论】:

    • 虽然这是一个旧答案,但在使用 removeAll 时要小心 - 它具有指数运行时间(最大值),具体取决于要删除的元素数量。
    • RemoveAll 趋向于 O(n^2),因为它重复使用 contains 方法。
    • @user2780757 距离 O(e^n) 还很远
    • 哦,哈哈,我明白了,为此道歉。只要一个人明白这一点,我就没事:-)
    【解决方案2】:

    是不是每次只能调用一次 iterator.remove() iterator.next() 调用,并且在下一次之前调用 remove 两次 调用 iterator.next() 导致了这个?

    如果你在没有next() 的情况下调用remove(),你会得到IllegalStateException

    您收到 ConcurrentModificationException 是因为您在同一个 ArrayList 上使用了 2 个迭代器,并且它们都从列表中删除了元素。

    如果您想从列表中删除重复项,可以使用 @OldCurmudgeon 代码或仅使用这 2 行代码:

    bets.clear();
    bets.addAll(new LinkedHashSet(bets));
    

    【讨论】:

      【解决方案3】:

      当在 Java 中使用 remove(int i) (即使用索引)或 remove(Object o) 从 ArrayList 中删除元素时,必须在底层数组中填充因删除元素而产生的间隙。这是通过将任何后续元素向左移动(从它们的索引中减去一个)来完成的。 System.arrayCopy 方法用于此。过程也可以称为洗牌。

      System.arraycopy(elementData, index+1, elementData, index, numMoved);
      

      这里 index+1 是源位置,index 是目标位置。由于索引位置的元素被删除,所以从 index+1 开始的元素被复制到从 index 开始的目标。

      您在循环中执行此操作,Arraylist 很难在给定时刻保持确定的状态,因此可能会丢失一些更改,为避免这种情况,我们有 Concurrentmodification 异常。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-12-14
        • 2014-06-03
        • 2020-08-23
        • 2013-03-02
        • 1970-01-01
        • 2013-04-17
        • 2014-05-04
        相关资源
        最近更新 更多