【问题标题】:LINQ .Where query takes 5+ minutes to executeLINQ .Where 查询需要 5 分钟以上才能执行
【发布时间】:2019-11-12 18:36:54
【问题描述】:

我需要过滤List<object>,以便删除另一个List<string> 中不存在string 属性的任何项目。

我创建这个控制台应用程序只是为了确保我的 LINQ 语法正确:

class FooBar
{
    public int Id { get; set; }
    public string ValueName { get; set; }
}

然后……

List<FooBar> foobars = new List<FooBar>
{
    new FooBar { Id = 1, ValueName = "Val1" },
    new FooBar { Id = 2, ValueName = "Val2" },
    new FooBar { Id = 3, ValueName = "Val3" },
    new FooBar { Id = 4, ValueName = "Val4" }
};

List<string> myStrings = new List<string>
{
    "Val1",
    "Val3"
};

// Only keep records where ValueName is found in `myStrings`
foobars = foobars.Where(f => myStrings.Contains(f.ValueName)).ToList();

所以,这一行:

foobars = foobars.Where(f => myStrings.Contains(f.ValueName)).ToList();

做的正是我想要的,它给了我这两条记录:

{ Id = 1, ValueName = "Val1" }
{ Id = 3, ValueName = "Val3" }

一切都好。但是......在实际应用中,foobars 有超过 200k 个项目,myStrings 有大约 190k。而当执行该 LINQ 行时,需要 5 分钟以上才能完成。

我显然做错了什么。 20 万条记录并没有那么大。而真正的FooBar 并没有那么复杂(没有嵌套对象,只有 9 个属性)。

这是怎么回事?

【问题讨论】:

  • 你的列表都排序了吗?因为this而问
  • 如果它在内存中,那似乎很长一段时间。如果您实际上正在做的是访问数据库并使用Contains,那么您将撤回 190k 条记录,同时避免使用数据库的索引。你在使用数据库吗?
  • 使用HashSet&lt;string&gt; myStrings 而不是List&lt;string&gt;
  • 2*4 与您的实际代码几乎相当,它执行 200K*190K 字符串比较 (38,000,000,000)。使用更好的数据结构,例如 HashSet。
  • @JustinLessard,不,没有排序。我会将它们都排序并重试。

标签: c# .net performance linq


【解决方案1】:

这里的问题是您正在执行 foobars.Where(f =&gt; myStrings.Contains(f.ValueName)) ,因此对于 foobars 中的每个项目,您都在检查 myStrings 的 所有 项目。

这是二次方的。也称为 O(n^2),阅读更多 here。 因此,如果您有 10+10 个项目,则进行 100 次检查(10*10),如果您有 10,000+10,000 个项目,您将进行 100,000,000 次检查。 在您的情况下,您正在进行 38,000,000,000+ 次检查;)

解决方案:从myStrings 创建一个HashSet 并使用HashSet 中的Contains。

例如替换为:

var myStringsSet = new HashSet<string>(myStrings);
foobars = foobars.Where(f => myStringsSet.Contains(f.ValueName)).ToList();

现在有 10,000+10,000 个项目,您将进行 10,000 次检查而不是 100,000,000 次。在您的情况下,这将是 200,000 张支票,而不是 38,000,000,000。

【讨论】:

  • 好的,谢谢!测试。测试完成后我会返回并报告(需要几分钟)
  • 成功了!在几毫秒内得到我想要的结果。谢谢!
  • 另外,你必须住在欧洲吗?你正在使用 .而不是 , 来分隔数千。
  • 谢谢,修复了千位运算符。来自荷兰的欢呼声 ;)
猜你喜欢
  • 2018-02-13
  • 2016-01-30
  • 2018-07-20
  • 2017-12-26
  • 2016-04-22
  • 1970-01-01
  • 1970-01-01
  • 2016-01-05
  • 1970-01-01
相关资源
最近更新 更多