LINQ扩展方法的顺序不影响性能?

时间:2012-04-11 16:26:34

标签: c# .net linq

我很惊讶,无论我是在前置还是附加LINQ扩展方法都没关系。

使用Enumerable.FirstOrDefault进行测试:

  1. hugeList.Where(x => x.Text.Contains("10000")).FirstOrDefault();
  2. hugeList.FirstOrDefault(x => x.Text.Contains("10000"));

    var hugeList = Enumerable.Range(1, 50000000)
        .Select(i => new { ID = i, Text = "Item" + i });
    
    var sw1 = new System.Diagnostics.Stopwatch();
    var sw2 = new System.Diagnostics.Stopwatch();
    
    sw1.Start();
    for(int i=0;i<1000;i++)
        hugeList.Where(x => x.Text.Contains("10000")).FirstOrDefault();
    sw1.Stop();
    
    sw2.Start();
    for(int i=0;i<1000;i++)
        hugeList.FirstOrDefault(x => x.Text.Contains("10000"));
    sw2.Stop();
    
    var result1 = String.Format("FirstOrDefault after: {0} FirstOrDefault before: {1}", sw1.Elapsed,  sw2.Elapsed);
    //result1: FirstOrDefault after: 00:00:03.3169683 FirstOrDefault before: 00:00:03.0463219
    
    sw2.Restart();
    for (int i = 0; i < 1000; i++)
        hugeList.FirstOrDefault(x => x.Text.Contains("10000"));
    sw2.Stop();
    
    sw1.Restart();
    for (int i = 0; i < 1000; i++)
        hugeList.Where(x => x.Text.Contains("10000")).FirstOrDefault();
    sw1.Stop();
    
    var result2 = String.Format("FirstOrDefault before: {0} FirstOrDefault after: {1}", sw2.Elapsed, sw1.Elapsed);
    //result2: FirstOrDefault before: 00:00:03.6833079 FirstOrDefault after: 00:00:03.1675611
    
    //average after:3.2422647 before: 3.3648149 (all seconds)
    
  3. 我猜想在Where之前加载会更慢,因为它必须找到所有匹配的项目然后取第一个而前面的FirstOrDefault可能会产生第一个找到的项目。

    问:有人可以解释我为什么走错了路吗?

2 个答案:

答案 0 :(得分:48)

  

我会猜到前缀会比较慢,因为它必须找到所有匹配的项目,然后取第一个和前面的FirstOrDefault可以产生第一个找到的项目。有人可以解释为什么我走错了路吗?

你的错误是因为你的第一句话是错误的。在获取第一个匹配项之前, Where“按需”获取匹配的项目;如果你只要求第一个,它只取出第一个。如果你只要求前两个,它只取得前两个。

Jon Skeet在舞台上做得很好。想象一下,你有三个人。第一个人有一张洗牌的牌。第二个人有一件T恤,上面写着“卡片是红色的”。第三个人戳了第二个人说“给我第一张牌”。第二个人一遍又一遍地捅第一个人直到第一个人交出红牌,然后第二个人交给第三个人。第二个人没有理由继续戳第一个人;任务完成了!

现在,如果第二人的T恤上写着“按等级递增排序”,那么我们的情况会有很大差异。现在,第二个人确实需要从第一个人那里获得每张卡,以便在将第一张卡交给第三人之前找到套牌中最低的卡。

现在,这应该为您提供必要的直觉,告诉您订单何时因性能原因而重要。 “给我红牌然后对它们进行排序”的最终结果与“对所有卡片进行分类然后给我红色”完全相同,但前者要快得多,因为你不必花时间整理你要丢弃的黑卡。

答案 1 :(得分:11)

Where()方法使用延迟执行,并将根据请求提供下一个匹配项 。也就是说,Where()不会评估并立即返回所有候选对象的序列,它们在迭代时一次提供一个。

由于FirstOrDefault()在第一个项目之后停止,这将导致Where()停止迭代。

FirstOrDefault()视为暂停Where()的执行,就好像它执行break一样。当然,这并不是那么简单,但实质上,因为FirstOrDefault()一旦找到项目就停止迭代,Where()不需要继续进行。

当然,这是在FirstOrDefault()条款上应用Where()的简单情况,如果您有其他条款暗示需要考虑所有项目,这可能会产生影响,但在将Where().FirstOrDefault()' combo or just FirstOrDefault()'与谓词一起使用时都是如此。