如何在遍历集合时安全地从集合中删除其他元素

6
我正在遍历一个强制实施快速失败迭代器概念的JRE集合,因此如果在迭代时修改了集合(除使用Iterator.remove()方法外),则会抛出ConcurrentModificationException异常。但是,如果对象满足条件,我需要删除对象的“逻辑伙伴”,从而防止该伙伴也被处理。我该怎么做?也许可以使用更好的集合类型来实现这个目的?
示例:
myCollection<BusinessObject>

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

感谢您的选择。

可能是Java:在迭代集合时有效地删除等效项的重复问题。 - McDowell
7个回答

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

编辑:重新阅读问题后发现此方法行不通,但我将其保留在此处,作为避免一般情况下出现此问题的示例。

您需要的是像这样的东西:
for (Iterator<BusinessObject> iter = myCollection.iterator; iter.hasNext(); )
{
    BusinessObject anObject = iter.next();
    if (someConditionIsTrue) 
    { 
        iter.remove();
    }        
}

如果您通过Iterator本身删除对象,它会意识到删除操作并且一切都按照您的预期进行。请注意,虽然我认为所有标准集合在这方面都表现得很好,但是Iterator不需要实现remove()方法,因此如果您无法控制myCollection的类(因此返回的迭代器的实现类),则可能需要在其中添加更多的安全检查。
另一种方法(例如,如果您无法保证迭代器支持remove()并且需要此功能)是创建一个要迭代的集合的副本,然后从原始集合中删除元素。
编辑:您可能可以使用后一种技术来实现您想要的内容,但是随后您仍然会回到迭代器首先抛出异常的原因:如果您删除了尚未到达的元素,迭代应该怎么做?删除(或不删除)当前元素相对明确定义,但是您谈论删除当前元素的伙伴,我想这可能在可迭代对象的任意点上。由于没有明确的处理方式,因此您将需要自己提供某种形式的逻辑以应对此问题。在这种情况下,我倾向于在迭代过程中创建和填充新集合,然后在结束时将其分配给myCollection变量。如果不可能,请跟踪要删除的伙伴元素并调用myCollection.removeAll()。

我认为你需要使用替代方法,因为被移除的对象是anObjectsPartner(而不是anObject),我理解它可能会在列表中较早或较晚出现。 - Alex B
是的,我刚刚意识到了并做了适当的编辑。这确实让情况变得更棘手了... - Andrzej Doyle

8

您想从列表中删除一个项目,并继续在同一列表上进行迭代。您能否实现一个两步解决方案,在步骤1中,您收集要删除的项目到一个中间集合中,在步骤2中标识它们后再将它们删除?


3

以下是一些思考(这取决于集合中两个对象之间的确切关系):

  1. 使用对象作为键,配对对象作为值的Map。
  2. 使用CopyOnWriteArrayList,但需要注意何时遇到配对对象。
  3. 将其复制到另一个Collection对象中,并迭代其中一个,删除另一个。如果原始Collection可以是Set,则在删除方面肯定会有帮助。

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);

1
CopyOnWriteArrayList 会做你想要的。

0
为什么不使用原始BusinessObject的Collection以及一个单独的类(比如Map),将它们关联起来(即创建一个Partner)?将它们作为组合元素放在自己的类中,这样你可以在删除Business object时总是删除其Partner。不要让调用者每次需要从集合中移除BusinessObject都去处理它。
class BusinessObjectCollection implements Collection<BusinessObject> {
  Collection<BusinessObject> objects;
  Map<BusinessObject, BusinessObject> associations;

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

0

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接