如何在迭代Collection时安全地从Collection中删除其他元素

时间:2009-05-26 16:02:19

标签: java collections

我正在迭代JRE Collection,它强制执行fail-fast迭代器概念,因此如果ConcurrentModificationException在迭代时被修改,则抛出Collection,而不是使用Iterator.remove()方法。但是,如果对象满足条件,我需要删除对象的“逻辑伙伴”。从而防止合作伙伴被处理。我怎样才能做到这一点?也许为此目的使用更好的收集类型?

实施例

myCollection<BusinessObject>

for (BusinessObject anObject : myCollection) 
{ 
  if (someConditionIsTrue) 
  { 
    myCollection.remove(anObjectsPartner); // throws ConcurrentModificationException 
  }
}

感谢。

7 个答案:

答案 0 :(得分:9)

这不是集合的错,它就是你使用它的方式。在迭代中途修改集合会导致此错误(这是一件好事,因为迭代通常无法明确地继续)。

编辑:重新阅读问题后,这种方法将无效,但我将其留在这里作为一般案例中如何避免此问题的示例。

你想要的是这样的:

for (Iterator<BusinessObject> iter = myCollection.iterator; iter.hasNext(); )
{
    BusinessObject anObject = iter.next();
    if (someConditionIsTrue) 
    { 
        iter.remove();
    }        
}

如果您通过Iterator本身删除对象,它会知道删除并且一切都按预期工作。请注意,虽然我认为所有标准集合在这方面都能很好地工作,但是迭代器不是必需来实现remove()方法,所以如果你无法控制myCollection的类(从而返回的迭代器的实现类)你可能需要在那里加入更多的安全检查。

另一种方法(比如说,如果你不能保证迭代器支持remove()而你需要这个功能)就是创建一个 copy 集合来迭代,然后删除来自原始集合的元素。

编辑:您可以使用后一种技术来实现您想要的功能,但是您最终还是会回到迭代器首先抛出异常的原因:什么如果删除尚未到达的元素,迭代会怎样?删除(或不删除)当前元素是相对明确定义的,但是您要谈论删除当前元素的伙伴,我认为它可能位于iterable中的随机点。由于没有明确的方法可以处理这个问题,因此您需要自己提供某种形式的逻辑来应对这种情况。在这种情况下,我倾向于在迭代期间创建和填充新集合,然后在最后将其分配给myCollection变量。如果无法做到这一点,那么跟踪要删除的合作伙伴元素并调用myCollection.removeAll将是最佳选择。

答案 1 :(得分:8)

您想要从列表中删除项目并继续在同一列表上进行迭代。您是否可以实施两步解决方案,在步骤1中,您收集要在临时收集中删除的项目,并在步骤2中识别它们后删除它们?

答案 2 :(得分:3)

一些想法(取决于集合中两个对象之间的关系到底):

  1. 以对象为键,伙伴为值的地图。
  2. 一个CopyOnWriteArrayList,但是当你点击合作伙伴时你必须注意到
  3. 将副本复制到另一个Collection对象中,并迭代一个,删除另一个。如果这个原始集合可以是一个集合,那将非常有助于删除。

答案 3 :(得分:1)

您可以尝试先找到要删除的所有项目,然后在处理完整个列表后删除它们。在找到它们时跳过已删除的项目。

myCollection<BusinessObject>
List<BusinessObject> deletedObjects = new ArrayList(myCollection.size());

for (BusinessObject anObject : myCollection) 
{ 
  if (!deletedObjects.contains(anObject))
  {
      if (someConditionIsTrue) 
      { 
          deletedObjects.add(anObjectsPartner);
      }
  }
}
myCollection.removeAll(deletedObjects);

答案 4 :(得分:1)

CopyOnWriteArrayList可以做你想要的。

答案 5 :(得分:0)

为什么不使用所有原始BusinessObject的集合,然后使用一个关联它们的单独类(例如Map)(即创建合作伙伴)?将它们作为复合元素放在它自己的类中,以便在删除Business对象时始终可以删除Partner。每次需要从Collection中删除BusinessObject时,都不要让调用者负责。

IE

class BusinessObjectCollection implements Collection<BusinessObject> {
  Collection<BusinessObject> objects;
  Map<BusinessObject, BusinessObject> associations;

 public void remove(BusinessObject o) {
  ...
// remove from collection and dissasociate...
 }
}

答案 6 :(得分:0)

最好的答案是第二个,使用迭代器。