【问题标题】:How to subtract one huge list from another efficiently in C#如何在 C# 中有效地从另一个列表中减去一个巨大的列表
【发布时间】:2011-07-02 19:00:10
【问题描述】:

我有一个很长的 Id(整数)列表,代表了我数据库中当前的所有项目:

var idList = GetAllIds();

我还有另一个巨大的通用列表,其中包含要添加到数据库中的项目:

List<T> itemsToAdd;

现在,我想从通用列表中删除其 Id 已在 idList 中的所有项目。 目前 idList 是一个简单的数组,我减去这样的列表:

itemsToAdd.RemoveAll(e => idList.Contains(e.Id));

我很确定它可能会快很多,那么我应该为这两个集合使用什么数据类型以及减去它们的最有效做法是什么?

谢谢!

【问题讨论】:

  • 如果可能的话,我也想知道如何流式传输/枚举它......

标签: c# performance collections


【解决方案1】:

假设以下前提为真:

  • idListitemsToAdd 不得包含重复值
  • 您使用的是 .NET Framework 4.0

您可以这样使用HashSet<T>

var itemsToAddSet = new HashSet(itemsToAdd);
itemsToAddSet.ExceptWith(idList);

根据文档,ISet<T>.ExceptWith 方法非常有效:

这个方法是一个 O(n) 操作, 其中 n 是元素的数量 其他参数。

在您的情况下,nidList 中的项目数。

【讨论】:

  • 问题在于 itemsToAdd 的类型是 HashSet,而 idList 的类型是 HashSet。因此我不能在这两个上调用 exceptWith 并且需要将 idList 转换为 Hashset 会消耗大量内存。
  • idList 不必是HashSet&lt;int&gt;,您只需从itemsToAdd 创建一个HashSet。然后您将idList 传递给HashSet&lt;T&gt;.ExceptWith 作为IEnumerable&lt;T&gt;
【解决方案2】:

暂时将idList 转换为HashSet&lt;T&gt; 并使用相同的方法,即:

items.RemoveAll(e => idListHash.Contains(e.Id));

应该会快很多

【讨论】:

  • 谢谢 - 这确实执行得更快,这就是我所做的!
【解决方案3】:

LINQ 可以提供帮助:

itemsToAdd.Except(idList)

您的代码很慢,因为List&lt;T&gt;.ContainsO(n)。所以你的总成本是O(itemsToAdd.Count*idList.Count)

您可以将 idList 设置为具有 O(1) .ContainsHashSet&lt;T&gt;。或者只是使用 Linq .Except 扩展方法,它会为您完成。

请注意,.Except 也会从左侧删除所有重复项。即新的int[]{1,1,2}.Except(new int[]{2}) 将只导致{1} 并且第二个1 被删除。但我认为您的情况没有问题,因为 ID 通常是唯一的。

【讨论】:

  • 请注意,这也会排除来自itemsToAdd 的任何重复项。这是否是一个问题取决于 OP(我怀疑不是,因为他们已经在他们的示例中使用了 RemoveAll)。
  • +1 感谢您的出色解释!我现在将 idList 构建为 Hashset,但不能使用 .Except(),因为 itemsToAdd 是 List/HashSet 类型,而 idList 是 HashSet 类型。不过它要快得多,并且可以满足我的需求。
【解决方案4】:

您应该使用两个HashSet&lt;int&gt;s。
请注意,它们是唯一且无序的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多