查找另一个列表中不存在的所有项目需要很多时间

Find all items that are not there in another list takes lot of time

我有几个通用列表,一个有超过 70,000 个项目,另一个有 10 个项目。与第一个列表进行比较时,我必须找出第二个列表中不存在的项目。从逻辑上讲,我的代码运行良好,并且我不会遇到较小列表集的任何严重性能问题。由于我的第一个列表超过 70K 个项目,我面临着严重的性能问题。执行和得到结果需要很多时间。

我的问题是?有没有更好的方法来做到这一点?我无法忍受这个性能问题。有什么改进建议吗?我正在使用 C#、.NET 3.5

List<Employee> existingEmployeeList = List of 70K employees;
List<Employee> validEmployeeList = List of 10 employees;

var emloyeeDeletedFilterList = existingEmployeeList.Where(m => !validEmployeeList.Any(p => p.EmployeeId == m.EmployeeId
                            && p.FirstName == m.FirstName
                            && p.Age == m.Age
                            && p.LastName == m.LastName));

我还有另一个操作要查找哪些是新添加到列表中的。

var emloyeeAddedFilterList = validEmployeeList.Where(m => !existingEmployeeList.Any(p => p.EmployeeId == m.EmployeeId
                                && p.FirstName == m.FirstName
                                && p.Age == m.Age
                                && p.LastName == m.LastName));

我在 where 子句中有 4 个条件来过滤员工列表。

编辑了我的问题:添加了一个代码片段

写一个自定义EqualityComparer<Employee> to compare on your 4 fields then use .Intersect(来做

var emloyeeFilterList = validEmployeeList.Intersect(existingEmployeeList, new EmployeeComparer()).ToList();

我认为在 validEmployeeList 而不是 existingEmployeeList 上调用 .Intersect( 会更快,但是我会测试这两种方式以确保。

更新:

糟糕,误解了你想要的内容。您想要使用的查询是 Except 而不是 Intersect.

如果您想要除有效员工之外的所有现有员工

var emloyeeFilterList = existingEmployeeList.Execpt(validEmployeeList, new EmployeeComparer()).ToList();

或者如果您想要除现有员工之外的所有有效员工。

var emloyeeFilterList = validEmployeeList.Execpt(existingEmployeeList, new EmployeeComparer()).ToList();

这里还有一个如何写的例子EmployeeComparer

public class EmployeeComparer : EqualityComparer<Employee>
{
    public override bool Equals(Employee x, Employee y)
    {
         return x.EmployeeId == y.EmployeeId
             && x.FirstName == y.FirstName
             && x.Age == y.Age
             && x.LastName == y.LastName
    }

    //Implmentation taken from 
    public override int GetHashCode(Employee obj)
    {
        unchecked // Overflow is fine, just wrap
        {
            int hash = (int) 2166136261;
            // Suitable nullity checks etc, of course :)
            hash = hash * 16777619 ^ obj.EmployeeId.GetHashCode();
            hash = hash * 16777619 ^ obj.FirstName.GetHashCode();
            hash = hash * 16777619 ^ obj.Age.GetHashCode();
            hash = hash * 16777619 ^ obj.LastName.GetHashCode();
            return hash;
        }
    }
}

考虑使用 HashSet 来存储元素。 Hash Set 要求您重载 class 的哈希函数。

此外,CPU 比较整数的速度比任何其他类型都快得多。考虑在遍历元素时实现更多的整数比较。

如果您对衡量性能感兴趣,请阅读有关评估程序相对性能的 Big O 方法。

节日快乐

从你的代码示例中我可以解释的是,你似乎只需要检查 'EmployeeId'。如果不是这种情况,您可以尝试以下方法来避免使用 LinQ,因为它不如显式迭代快。

List<Employee> employeeFilterList = new List<Employee>();

for(int a = validEmployeeList.Count; --a >= 0; )
{
    for(int b = existingFieldMappingList.Count; --b >= 0; )
    {
        Employee aEmployee = validEmployeeList[a];
        Employee bEmployee = validEmployeeList[b];
        if (aEmployee.EmployeeId != bEmployee.EmployeeId)
            continue;
        if (aEmployee.FirstName != bEmployee.FirstName)
            continue;
        if (aEmployee.Age != bEmployee.Age)
            continue;
        if (aEmployee.LastName != bEmployee.LastName)
            continue;

        employeeFilterList.Add(aEmployee);
    }
}

编辑:请记住,您可以按照第一个最有可能跳到下一次迭代的方式重新排序 IF 条件。