【问题标题】:Find all items that are not there in another list takes lot of time查找另一个列表中不存在的所有项目需要很多时间
【发布时间】:2015-12-12 02:19:40
【问题描述】:

我有几个通用列表,一个包含 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 个条件来过滤员工列表。

编辑了我的问题:又添加了一个代码 sn-p

【问题讨论】:

  • 如果您重新安排条件以从最严格到最不严格的条件进行检查,您应该能够加快速度。由于 70K 员工中的大多数都将通过 !validEmployeeList.Any 过滤器,因此您不妨将其放在条件的末尾。
  • Employee实体的主键是什么?不要因为使用 EF 而忘记数据库基础知识。 :) .

标签: c# linq lambda .net-3.5 generic-list


【解决方案1】:

编写自定义 EqualityComparer&lt;Employee&gt; 以比较您的 4 个字段,然后使用 .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 http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode
    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;
        }
    }
}

【讨论】:

  • 我需要找到现有员工列表中没有的项目。 intersect 会帮我做吗?我想知道,这只会给出 validEmployeesList 和 existingEmployeeList 之间的公共集合对吗?
  • @nimi 哦,抱歉更新了我的答案,你会使用Except。如果您有validEmployeeList.Any( 而不是!validEmployeeList.Any(,您将使用Intersect。我还提供了EmployeeComparer 的示例实现,以防您在编写它时需要帮助。
  • HashSet 不会快很多吗?
  • @TuukkaX except 和 Intercect 正在使用 hash sets internally(你看到的类 Set 有一个 HashSet 的特殊实现,它针对在 LINQ 中的使用进行了优化)。所以不,它们不会更快,因为它们使用比System.Collections.Generic中更快的特殊HashSets@
【解决方案2】:

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

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

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

节日快乐

【讨论】:

  • 仅供参考,像 IntersectExcept 这样的 LINQ 函数在内部使用 HashSet,因此它们非常高效。
【解决方案3】:

对于我可以从您的代码示例中解释的内容,您似乎只需要检查“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 条件,这样第一个条件最有可能跳到下一次迭代。

【讨论】:

  • 我需要检查所有条件。
  • 好的,使用我提供的示例,您能看到性能提升吗?如果没有,如果您提供这些列表的填充方式,我可以提供更好的方法。
  • 我已编辑我的问题以添加更多信息。我正在做你建议的改变。会及时通知您。
  • nic,您还有其他解决方案吗?我正在从数据库中获取现有员工列表,并且有效员工列表是从 UI 生成的。
  • LINQ 非常快,只要您使用正确的工具来完成这项工作,而不是使用 foo.Where( x=&gt; !bar.Any(foo.Equals(bar)) 使用 foo.Execpt(bar),您将获得巨大的性能提升。
猜你喜欢
  • 2012-07-10
  • 2017-06-06
  • 1970-01-01
  • 1970-01-01
  • 2022-11-12
  • 1970-01-01
  • 1970-01-01
  • 2021-12-03
  • 1970-01-01
相关资源
最近更新 更多