从ArrayList同时删除两个对象而不会导致ConcurrentModificationException?

时间:2014-10-11 20:06:00

标签: java iterator iteration concurrentmodification

我有以下代码抛出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两次导致这个?

任何帮助都会非常感谢。

3 个答案:

答案 0 :(得分: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);
}

答案 1 :(得分:0)

  

是否只能为每个调用iterator.remove()一次   iterator.next()调用,并在下一次调用之前调用remove   调用iterator.next()会导致这个吗?

如果您在没有remove()的情况下致电next(),您将获得IllegalStateException

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

如果要从列表中删除重复项,请使用@OldCurmudgeon代码或仅使用以下两行代码:

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

答案 2 :(得分:0)

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

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

此处index + 1是源位置,而index是目标位置。由于位置索引处的元素已删除,因此将从索引+1开始的元素复制到从索引开始的目标。

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