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

Java 如何在遍历集合时安全地从集合中删除其他元素,java,collections,Java,Collections,我正在迭代一个JRE集合,它强制执行fail-fast迭代器概念,因此如果在迭代过程中修改了集合,而不是使用迭代器.remove()方法,则会抛出一个ConcurrentModificationException。但是,如果对象满足条件,我需要删除对象的“逻辑伙伴”。从而防止伙伴也被处理。我该怎么做?也许通过使用更好的集合类型来实现这一目的 例如 myCollection<BusinessObject> for (BusinessObject anObject : myCollec

我正在迭代一个JRE
集合
,它强制执行fail-fast迭代器概念,因此如果在迭代过程中修改了
集合
,而不是使用
迭代器.remove()
方法,则会抛出一个
ConcurrentModificationException。但是,如果对象满足条件,我需要删除对象的“逻辑伙伴”。从而防止伙伴也被处理。我该怎么做?也许通过使用更好的集合类型来实现这一目的

例如

myCollection<BusinessObject>

for (BusinessObject anObject : myCollection) 
{ 
  if (someConditionIsTrue) 
  { 
    myCollection.remove(anObjectsPartner); // throws ConcurrentModificationException 
  }
}
myCollection
for(BusinessObject对象:myCollection)
{ 
如果(某个条件是真的)
{ 
myCollection.remove(anObjectsPartner);//抛出ConcurrentModificationException
}
}

谢谢。

您想从列表中删除一个项目,然后继续在同一列表上迭代。您能否实施一个两步解决方案,即在步骤1中,您在临时收集中收集要删除的项目,在步骤2中,您在识别这些项目后将其删除?

这不是收集的错误,而是您使用它的方式。在迭代进行到一半时修改集合会导致此错误(这是一件好事,因为迭代通常不可能明确地继续)

编辑:重读了这个问题后,这种方法不会起作用,尽管我把它作为在一般情况下如何避免这个问题的一个例子放在这里

你想要的是这样的:

for (Iterator<BusinessObject> iter = myCollection.iterator; iter.hasNext(); )
{
    BusinessObject anObject = iter.next();
    if (someConditionIsTrue) 
    { 
        iter.remove();
    }        
}
for(迭代器iter=myCollection.Iterator;iter.hasNext();)
{
BusinessObject anObject=iter.next();
如果(某个条件是真的)
{ 
iter.remove();
}        
}
如果您通过迭代器本身删除对象,迭代器会意识到删除操作,并且一切都会按照您的预期进行。请注意,尽管我认为所有标准集合在这方面都能很好地工作,但迭代器不需要实现remove()方法,因此如果您无法控制
myCollection
的类(以及返回迭代器的实现类),您可能需要在其中进行更多的安全检查

另一种方法(例如,如果您不能保证迭代器支持
remove()
,并且您需要此功能)是创建要迭代的集合的副本,然后从原始集合中删除元素

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

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

  • 以对象为键、伙伴为值的映射
  • 一个CopyOnWriteArrayList,但你必须注意你何时击中了对方
  • 将副本复制到另一个集合对象中,并在其中一个对象上进行迭代,删除另一个对象。如果这个原始集合可以是一个集合,那肯定会有助于删除

  • 为什么不使用所有原始BusinessObject的集合,然后使用一个单独的类(如Map)将它们关联起来(即创建合作伙伴)?将这两个元素作为复合元素放在它自己的类中,以便在删除业务对象时始终可以删除合作伙伴。不要让调用方在每次需要从集合中删除BusinessObject时都承担责任

    类BusinessObjectCollection实现收集{
    收集对象;
    地图协会;
    公共无效删除(BusinessObject o){
    ...
    //从集合中删除并解除关联。。。
    }
    }
    
    您可以尝试先查找所有要删除的项目,然后在处理完整个列表后删除它们。在找到已删除的项目时跳过这些项目

    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);
    
    myCollection
    List deletedObjects=newArrayList(myCollection.size());
    for(BusinessObject对象:myCollection)
    { 
    如果(!deletedObjects.contains(anObject))
    {
    如果(某个条件是真的)
    { 
    deletedObjects.add(一个对象spartner);
    }
    }
    }
    myCollection.removeAll(删除对象);
    
    CopyOnWriteArrayList会做你想做的事。

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

    我认为你必须使用另一种方法,因为要删除的对象是一个对象,我知道它可能在列表中出现较早或较晚。是的,我刚刚意识到这一点并进行了适当的编辑。这确实让情况变得更棘手…可能是重复的
    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);