C# / .NET 比较两个大型列表并找出缺失的项

6

基本上,我有两个像下面这样的大列表:

public class Items
{
 public string ItemID { get; set; }
}


var oldList = new List<Items>(); // oldList

var newList = new List<Items>(); // new list

这两个列表都非常大,如果它们都很大(超过30秒),简单的双重foreach将不足以满足执行时间。

在我之前在stackoverflow上提出的问题中,有人回答了如何比较这两个相同列表并找到哪些项目具有不同的QuantitySold参数,然后将其存储在第三个名为“DifferentQuantityItems”的列表中,如下所示:

var differentQuantityItems =
    (from newItem in newList
     join oldItem in oldList on newItem.ItemID equals oldItem.ItemID
     where newItem.QuantitySold != oldItem.QuantitySold
     select newItem).ToList();

现在我想从这两个列表中得到以下内容:
- A list of items that are present in newList, but not in oldList

- A list of items that are present in oldList, but not in newList

我该如何实现这个?有人可以帮助我吗?

注:我会通过属性“ItemID”来“知道”其中一个列表中是否缺少任一项目...


有人吗?=) - User987
你可以尝试使用LINQintersectionunion。https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.intersect?view=netframework-4.7.2 - Aarif
你可以使用自定义比较器类的 Equals 方法。 - Aarif
@Aarif哦,当我在其中一篇回复中看到并行循环被提到时,我立刻逃走了...我有并行循环的不好经历哈哈 =D - User987
你没有说清楚列表是否已排序。在选择方法之前了解这一点非常重要。 - codenheim
显示剩余2条评论
5个回答

4

编辑

Except 方法的执行速度更快。您可以在 这里 阅读相关性能信息。

var missedOld = oldList.Except(newList, new ItemsEqualityComparer());
var oldList= oldList.Except(missedOld, new ItemsEqualityComparer());

旧回答

两个不同的列表,其中包含了一些缺失的项目。

var missedOld = oldList.Where(x => !newList.Select(i => i.ItemID).Contains(x.ItemID)) 
var missedNew = newList.Where(x => !oldList.Select(i => i.ItemID).Contains(x.ItemID))

一个列表中的所有未完成项目:

oldList.Concat(newList).GroupBy(x => x.ItemID).Where(x => x.Count() < 2).Select(x => x.Value).ToList()

你不应该创建一个新变量并将新值存储在其中吗? - User987
@User987 当然,你应该将这个表达式的值放入新变量中,然后使用它。 - Akmal Salikhov
1
@User987 你也可以通过属性进行比较,像这样:var missedOld = oldList.Where(x => !newList.Select(l => l.ItemID).Contains(x.ItemID)) - Akmal Salikhov
是的,就是这样 :) ... 你能把那个回复发出来让其他人也看到吗? - User987
@codenheim,你说得非常正确!感谢你的周到考虑。我会编辑我的答案。 - Akmal Salikhov
显示剩余5条评论

2

看起来不错,如果可以的话,能给我展示一个例子吗? :) - User987
1
同意尝试使用Except(),因为它应该具有O(1)或O(log N)的复杂度,这是由于内部使用了HashSet(虽然我不是100%确定,但在测试中似乎是这样)。 - codenheim

1
var items = new List<int>(oldList.Select(x => x.ItemID ));
var missingValues = newList.Where(x => !diffids.Contains(x.ItemID)).ToList();

你也可以使用 except。

嗯,这个对我有点困惑...我能提取类形式的项目吗?不仅仅是整数值.. :D - User987

0
如果列表足够大,嵌套循环需要30秒以上的时间,我建议您将每个列表的项目放入相应的HashSet中,并使用它来查找异常。哈希表的规模为O(1)或O(log N),而比较两个未排序的列表则为O(n^2)。
话虽如此,尝试使用Linq Except()。
var notinNewList = oldList.Except(newList);

如果我没记错的话,.Except() 的内部实现依赖于 HashSets。

其次,如果列表已经排序或者可以预先排序,那么你可以进行一次线性遍历而不需要嵌套循环,这可能比任何其他方法都要快。

我不建议使用 List.Contains(),因为它是一个线性实现,会导致与你试图避免的 O(n^2) 相同的结果,尽管由于 Linq 语法糖的缘故,它看起来更漂亮。


-1
var items = newList.Where(n => !oldlist.Any(o => o.ItemID == n.ItemID)).ToList();

由于您无需再次访问数据库并且不使用包含(类似于SQL中的like),因此它更加灵活,并且也在同一行中。


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