【问题标题】:Most efficient way to remove items from a list without running into a collection modified exception?从列表中删除项目而不遇到集合修改异常的最有效方法?
【发布时间】:2011-07-07 19:43:32
【问题描述】:

我有 2 个列表:

ListA { 'A', 'B', 'C' } //ListA is DictA.Keys
ListB { 'B', 'X', 'Y' } //ListB is DictB.Keys

如果我执行ListA.Except(ListB),我会返回一个异常迭代器,这将使我可以遍历 ListA 以获取不在 ListB 中的任何项目。问题是它的实现也是简单地使用 ListA (出于某种原因,我认为它会创建一个新的项目集合,这些项目是不同的)。好吧,我发现它仍然使用 ListA 作为源,但只是使用了一种特殊类型的迭代器。所以,当然,当我从 ListA 中删除一个项目时,它会抱怨该集合已被修改。

我可以想出几种方法来做我想做的事,首先是复制 ListA 并在副本上执行 except。第二个是只做一个while循环。我只是想知道这个问题的最佳解决方案是什么,以及什么遵循标准准则。

如果我以错误的方式处理此问题,我很想知道这一点。我的主要目标是使用键作为比较从 DictA 中删除不在 DictB 中的所有内容。

【问题讨论】:

    标签: c# linq dictionary ienumerable set-difference


    【解决方案1】:

    如果您需要保留结果并独立于以后的更改,只需在查询中调用ToList()ToArray() 即可获得具体结果。

    var query = list1.Except(list2).ToList(); 
    

    对任一源输入的更改都不会影响您现在已全面评估的查询。

    【讨论】:

    • 嗯,我在想这个,但我认为它比执行 while 循环更昂贵。嗯,它更简单:)
    • @myermian - 有一个权衡。调用ToList()ToArray() 会遍历整个查询,然后(当然)迭代结果,这是一个(可能更小的)遍历。如果涉及的列表很大,那么您将受到处罚。根据列表中对象的类型,您还可以分配更多内存来存储值或引用。但是,如果您要进行查询并且在迭代它时希望修改底层源,这是一个相当简单的机制,可以让您这样做。
    • 好吧,我认为 ToList()/ToArray() 可能会更好。因为列表是使用IEnumerable<T> 生成的,所以该列表保证是正确的结果(集合无法更改)。然后,之后我只是去删除一个项目(如果它仍然存在)。执行 while 循环的另一种方法可能是在修改集合时循环的多线程噩梦。所以是的,我想我会坚持使用 ToArray() 来获取我的键列表。 :)
    【解决方案2】:

    为什么不直接使用急切评估?

    var myList = ListA.Except(ListB).ToList();
    

    【讨论】:

      【解决方案3】:

      出于同样的原因,我使用下一个:

      ListA = ListA.Except(ListB).ToList();
      

      【讨论】:

        【解决方案4】:

        也许你会倒退。另一种说法是“从 DictA 中删除不在 DictB 中的所有内容”是说“保留 DictA 中也在 DictB 中的所有内容”。与其尝试从 ListA 中删除内容,不如创建一个新列表:

        ListA.Join(ListB, ...);
        

        【讨论】:

        • 嗯,我认为用新的字典替换原来的字典比单独从原来的字典中删除项目的成本更高。
        • 可能是这样,但我认为您无法避免在这种情况下分配某种新结构。您会注意到其他答案已建议您创建列表或数组。无论如何,代码的清晰度可能值得额外的开销。这实际上取决于您执行此操作的环境。
        【解决方案5】:

        如果您可以控制相关数据结构,那么定义一个接受谓词(接受列表的数据类型并返回布尔值的函数)并删除谓词所在的所有项目的 Purge 方法可能会很有用返回 true(我希望 Microsoft 已经定义了一个 IPurgeableCollection,因为对于许多正常的 Microsoft 集合实现这样的例程不会存在固有的困难)。请注意,在许多情况下,集合实现 Purge 方法比允许在枚举期间进行一般修改要容易得多,而且在许多情况下,这样的方法不仅可以避免创建数据的额外副本以被删除,但它也可以大大减少执行删除所需的工作量(例如,在清除字典时,不必查找要删除的每个键,因为在枚举期间已经找到它)。

        【讨论】:

          【解决方案6】:

          如果您想要两个字典的交集,那么您可以这样做:

          IEnumerable<KeyValuePair<Char, String>> result = DictB.Intersect<KeyValuePair<Char, String>>(DictA);

          这将返回 DictA 中与 DictB 匹配的所有项目。此外,对于示例,我假设键是字符类型,值是字符串类型。如果您的不同,只需为您的解决方案设置正确的类型。 要查看实际结果,您必须发送 result.GetEnumerator() 或在 foreach 语句中使用它。

          【讨论】:

            猜你喜欢
            • 2014-06-24
            • 2013-01-17
            • 2010-09-17
            • 1970-01-01
            • 2015-08-02
            • 1970-01-01
            • 2021-02-15
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多