【问题标题】:How to safely remove other elements from a Collection while iterating through the Collection如何在遍历集合时安全地从集合中删除其他元素
【发布时间】:2010-10-29 00:41:04
【问题描述】:

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

示例。

myCollection<BusinessObject>

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

谢谢。

【问题讨论】:

标签: java collections


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    CopyOnWriteArrayList 会做你想做的事。

    【讨论】:

      【解决方案3】:

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

      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】:

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

        编辑:重新阅读问题后,这种方法将不起作用,尽管我将其留在这里作为在一般情况下如何避免此问题的示例。

        你想要的是这样的:

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

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

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

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

        【讨论】:

        • 我认为您将不得不使用替代方法,因为要删除的对象是 anObjectsPartner(而不是 anObject),据我所知,它可能会在列表中更早或更晚出现。
        • 是的,我刚刚意识到这一点并进行了适当的编辑。它确实使情况更加棘手......
        【解决方案5】:

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

        浏览器

        class BusinessObjectCollection implements Collection<BusinessObject> {
          Collection<BusinessObject> objects;
          Map<BusinessObject, BusinessObject> associations;
        
         public void remove(BusinessObject o) {
          ...
        // remove from collection and dissasociate...
         }
        }
        

        【讨论】:

          【解决方案6】:

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

          1. 以对象为键、伙伴为值的 Map。
          2. 一个CopyOnWriteArrayList,但是你打伙伴的时候要注意
          3. 将副本复制到不同的 Collection 对象中,然后迭代一个,删除另一个。如果这个原始 Collection 可以是一个 Set,那肯定会有助于删除。

          【讨论】:

            【解决方案7】:

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2015-10-05
              • 2011-02-06
              • 1970-01-01
              • 1970-01-01
              • 2017-08-09
              • 2011-06-12
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多