20

我很惊讶我是否预先或附加 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)
    

我猜想它会更慢,Where因为它必须找到所有匹配的项目,然后取第一个,而前面的FirstOrDefault可以产生第一个找到的项目。

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

4

2 回答 2

49

我猜想在 Where 之前添加会更慢,因为它必须找到所有匹配的项目然后取第一个,而前面的 FirstOrDefault 可以产生第一个找到的项目。有人可以解释为什么我在错误的轨道上吗?

您走错了路,因为您的第一个陈述根本不正确。不需要Where在获取第一个匹配项之前找到所有匹配项。“按需”获取匹配项;如果您只要求第一个,它只会获取第一个。如果您只要求前两个,它只会获取前两个。Where

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

现在,如果第二个人的 T 恤上写着“按等级升序排列”,那么我们的情况就大不相同了。现在第二个人确实需要从第一个人那里拿到每张牌,以便找到牌堆中最低的牌,然后再将第一张牌交给第三个人。

现在,这应该为您提供必要的直觉,以判断何时出于性能原因订单确实很重要。“给我红卡然后排序”的最终结果与“排序所有卡然后给我红卡”完全相同,但前者要快得多,因为你不必花任何时间排序你要丢弃的黑牌。

于 2012-04-11T16:42:22.640 回答
11

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

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

FirstOrDefault()将 的 的执行想象成Where()它执行了break. 当然,这并不是那么简单,但本质上因为FirstOrDefault()一旦找到一个项目就会停止迭代,因此Where()不需要进一步进行。

当然,这是在子句FirstOrDefault()上应用 a 的简单情况Where(),如果您有其他子句暗示需要考虑所有项目,这可能会产生影响,但这在使用Where().FirstOrDefault()' combo or justFirstOrDefault()' 和谓词。

于 2012-04-11T16:31:44.677 回答