【问题标题】:Where-clause called more often than item count比项目计数更频繁地调用 where 子句
【发布时间】:2014-04-05 17:30:03
【问题描述】:

我有一个List<Order>,我正在尝试使用 LINQ 过滤它:

var grouped = from o in orders
  group o by o.OrderNumber into g
  select new { Id = g.Key, Orders = g };

var GroupedList = grouped.ToList();

int max = GroupedList.Count();
int count = 0;
var filtered =
    from g in GroupedList
    where IncrementProgress(max, ref count)
    select g.Id;

var filteredOrders = orders.Where(o => filtered.Contains(o.OrderNumber));

IncrementProgress 内部,我将countmax 打印到调试输出。 max 在我的测试 3500 中,我从 count 获得了 150000 和计数的输出。

有人知道为什么吗?

PS:在我的生产代码中有过滤逻辑而不是IncrementProgress

更新:

这里是IncrementProgress-方法:

private bool IncrementProgress(int max, ref int count)
{
    Debug.WriteLine("Filtering {0} of {1}", ++count, max);
    return true;
}

【问题讨论】:

  • 你能告诉我们IncrementProgress方法吗?

标签: c# linq linq-to-objects


【解决方案1】:

这是因为 LINQ 是惰性的,filtered 不是一个集合——它是一个内存查询,它只存储如何评估结果的信息,而不是结果本身。因此,每次您使用 filtered 时,都会再次对其进行评估,遍历 GroupedList 并再次检查 where 条件。

这意味着,where 条件将被评估orders.Count() * GroupedList.Count() 次。

ToList() 调用添加到filtered 以热切评估它。

var filtered =
    (from g in GroupedList
     where IncrementProgress(max, ref count)
     select g.Id).ToList();

但是,因为您以后只在filtered 上使用Contains,所以您应该使用HashSet<int> 来存储结果。这将使Contains 调用O(1) 而不是O(n),这应该会大大提高性能。

var filtered =
    new HashSet<int>(from g in GroupedList
                     where IncrementProgress(max, ref count)
                     select g.Id);

【讨论】:

  • 谢谢。由于您的额外优化,工作得更好更快!
【解决方案2】:

您的 LINQ 查询在您每次枚举过滤后的集合时执行,在您的情况下,每次调用 Contains 方法时。

尝试将过滤后的变量声明为(&lt;LINQ Query&gt;).ToArray()。 这将只枚举一次查询。

很抱歉格式不正确(手机)。 希望对您有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-04-22
    • 2011-03-19
    • 1970-01-01
    • 2022-08-14
    • 2016-01-23
    • 1970-01-01
    • 2014-09-04
    • 1970-01-01
    相关资源
    最近更新 更多