查找不在列表 A 但列表 B 中的人员的更快方法 [重复]

Posted

技术标签:

【中文标题】查找不在列表 A 但列表 B 中的人员的更快方法 [重复]【英文标题】:faster way to find persons not in list A but list B [duplicate] 【发布时间】:2021-07-13 09:20:13 【问题描述】:

今天我使用它来获取不在列表 A 中但在列表 B 中的人员列表。它有效,但似乎需要很长时间才能获得结果。有没有更快的方法来做同样的事情?

    var missingPeople = listofPersons.Where(p => allUsedPersons.All(p2 => p2.Id != p.Id)).ToList(); 

【问题讨论】:

你想要(伪代码)B.Except(A) 吗? 您熟悉计算时间复杂度吗? 你试过Except吗? docs.microsoft.com/en-us/dotnet/api/… allUsedPersons是什么类型? 【参考方案1】:

您当前的实现有 O( n * m ) time complexity。

其中nlistofPersons 的基数。 其中mallUsedPersons 的基数。 因此,如果您有 500 个listofPersons 和 200 个allUsedPersons,您的代码将接受 100,000 次检查。 这很糟糕。 这是因为 Linq 的 Where 将针对 listofPersons 中的每个项目运行,并且在您的 Where 内部有 allUsedPersons.All,它将运行 p2.Id != p.Id 检查 allUsedPersons 中的每个项目。李>

相反,使用HashSet<T>O(n) 时间构建一组已知值 - 然后您可以在O(1) 时间执行存在 检查。

因此,如果您有 500 个listofPersons 和 200 个allUsedPersons,我下面的代码将只需要 500 次检查。 100,000 与 500:找出差异
HashSet<Int32> allPeopleIds = listofPersons.Select( p => p.Id ).ToHashSet();

List<Person> missingPeople = allUsedPersons
    .Where( p => !allPeopleIds.Contains( p.Id ) )
    .ToList();

在关系代数中(或者它是关系微积分?)你在做什么is known as an anti-join and Linq supports it via the Except method,但是你需要定义一个自定义比较器,因为 Linq 没有 有一个ExceptBy方法(but MoreLinq does, though)。

【讨论】:

为什么是Int32 而不是int @Dominik 这是我个人的偏好。 Int32int 始终相同:***.com/questions/62503/should-i-use-int-or-int32 @TimSchmelter 很好,谢谢! Person 类中覆盖GetHashCode(假设列表项是此类自定义类的实例)可能会提高性能。同样,将 Equals 覆盖到值比较并仅评估被认为相关的字段/道具,值得浪费一两个想法。哈希码的唯一性是Except、Union、Intersect 和大多数HashSet 操作性能的关键。 @Dai 你的方法比我做的要快得多。感谢您的解决方案和解释【参考方案2】:

另一种选择是提供自定义的、可重复使用的IEqualityComparer&lt;Person&gt;

public class PersonIdComparer : IEqualityComparer<Person>

    public bool Equals(Person x, Person y)
    
        return x?.Id == y?.Id;
    

    public int GetHashCode(Person obj)
    
        return obj?.Id ?? int.MinValue;
    

您可以将它用于许多 LINQ 方法。在这种情况下,您应该将其用于Except

var missingPeople = listofPersons.Except(allUsedPersons, new PersonIdComparer()).ToList(); 

Except 非常高效,因为它使用类似于HashSet 的集合。

【讨论】:

【参考方案3】:

我认为以下方法可以帮助您提高性能:

List<int> listOfIds = allUsedPersons.select(c => c.Id).ToList();
var missingPeople = listofPersons.Where(r=>!listOfIds.Contains(r.Id));

【讨论】:

这个答案仍然是O(n*m),因为List&lt;int&gt;.Contains 仍然是O(n)。您需要使用基于哈希表的集合(例如 HashSet&lt;T&gt;Dictionary&lt;K,V&gt; 以获得 O(1) 性能)。或者,使用基于树的集合将为您提供O( log n ) 性能,这仍然是一个改进,尽管对于这个特定的应用程序来说不是那么好。 感谢@Dai 的解释,我真的不知道,非常感谢。【参考方案4】:

allUsedPersons.All() 之类的东西放在谓词中会不必要地增加迭代次数。事先准备好必填字段列表(此处为 p.id)并在谓词中使用它。

假设 allUsedPersons 是列表 A,下面会更快

var usedPersonsId = allUsedPersons.Select(p => p.id).ToList();

var missingPeople = listofPersons.Where(p => !usedPersonsId.Contains(p.Id)).ToList(); 

【讨论】:

这个答案 still 与另一个答案一样具有O(n*m) 时间复杂性:不要使用ToList()。幸运的是,我要补充一点,如果您要对 usedPersonsId 进行排序,那么您可以使用二进制搜索而不是 Contains 检查,这将为您提供 O( log n ) 运行时 - 更好,但仍然不是最好的@ 987654328@运行时。

以上是关于查找不在列表 A 但列表 B 中的人员的更快方法 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

在列表/数组中查找元素的更快方法

如何清除不在列表中的输入[重复]

二分查找会更快吗?Python中的二分查找与线性查找性能测试

在 MongoDB 中,查找所有行 ID 不在给定列表中的记录的查询时间复杂度是多少?

TSQL按组查找不在另一个列表中的项目

python 玩转字符串,字典,列表排序,查找,去重