哪个更快:单个(谓词)或Where(谓词).单个()

Jon*_*art 33 .net c# linq

这个答案引起的讨论让我很好奇.哪个更快:

someEnumerable.Single(predicate);
Run Code Online (Sandbox Code Playgroud)

要么

someEnumerable.Where(predicate).Single();
Run Code Online (Sandbox Code Playgroud)

毕竟,第一个更短,更简洁,似乎是专门建造的.

甚至ReSharper建议前者:

在此输入图像描述

我在上一篇文章中争论说,它们在功能上是相同的,并且应该具有非常相似的运行时.

Jon*_*art 34

LINQ到对象

没有什么像这样的基准回答像这样的问题:

(更新)

class Program
{
    const int N = 10000;
    volatile private static int s_val;

    static void DoTest(IEnumerable<int> data, int[] selectors) {
        Stopwatch s;

        // Using .Single(predicate)
        s = Stopwatch.StartNew();
        foreach (var t in selectors) {
            s_val = data.Single(x => x == t);
        }
        s.Stop();
        Console.WriteLine("   {0} calls to Single(predicate) took {1} ms.",
            selectors.Length, s.ElapsedMilliseconds);

        // Using .Where(predicate).Single()
        s = Stopwatch.StartNew();
        foreach (int t in selectors) {
            s_val = data.Where(x => x == t).Single();
        }
        s.Stop();
        Console.WriteLine("   {0} calls to Where(predicate).Single() took {1} ms.",
            selectors.Length, s.ElapsedMilliseconds);
    }


    public static void Main(string[] args) {
        var R = new Random();
        var selectors = Enumerable.Range(0, N).Select(_ => R.Next(0, N)).ToArray();

        Console.WriteLine("Using IEnumerable<int>  (Enumerable.Range())");
        DoTest(Enumerable.Range(0, 10 * N), selectors);

        Console.WriteLine("Using int[]");
        DoTest(Enumerable.Range(0, 10*N).ToArray(), selectors);

        Console.WriteLine("Using List<int>");
        DoTest(Enumerable.Range(0, 10 * N).ToList(), selectors);

        Console.ReadKey();
    }
}
Run Code Online (Sandbox Code Playgroud)

有点令人震惊的是,.Where(predicate).Single()赢了大约两倍.我甚至两次运行两个案例以确保缓存等不是一个因素.

1) 10000 calls to Single(predicate) took 7938 ms.
1) 10000 calls to Where(predicate).Single() took 3795 ms.
2) 10000 calls to Single(predicate) took 8132 ms.
2) 10000 calls to Where(predicate).Single() took 4318 ms.
Run Code Online (Sandbox Code Playgroud)

更新结果:

Using IEnumerable<int>  (Enumerable.Range())
   10000 calls to Single(predicate) took 7838 ms.
   10000 calls to Where(predicate).Single() took 8104 ms.
Using int[]
   10000 calls to Single(predicate) took 8859 ms.
   10000 calls to Where(predicate).Single() took 2970 ms.
Using List<int>
   10000 calls to Single(predicate) took 9523 ms.
   10000 calls to Where(predicate).Single() took 3781 ms.
Run Code Online (Sandbox Code Playgroud)

  • 您的基准测试不会考虑"Single"的主要目的,检查最多只有一个匹配结果(在您的代码中绝不是这种情况,或者您需要异常处理).从理论上讲,`.Single(谓词)`应该更快做到这一点,因为它应该能够抛出第二个匹配,而不是首先枚举整个结果集. (7认同)
  • @Richard你错了,第二个会首先枚举整个结果集.LINQ使用延迟评估,因此这两个示例都不会枚举不必要的项目. (3认同)
  • 现在(2018)我再次测试这个,我得到了其他结果.在我的测试中,.single(..)更快,因为.where(..).single().但只是低于10%的因素 (3认同)
  • @Richard"而不是首先枚举整个结果集"......但这不是`Where()`的工作方式......它是懒惰的`yield`ing结果,因为他们要求......在这种情况下通过`Single()`调用`ToNext()`. (2认同)

man*_*lds 8

Where(predicate).Single() 会比...更快 Single(predicate)

编辑:你会期望Single()Single(predicate)以类似的方式编码,但事实并非如此.Single()一找到另一个元素就会完成,但后者会找到所有令人满意的元素.

感兴趣的(原来的答案)附加点- Where做特殊优化为不同类型的集合类型,而其他方法一样First,SingleCount没有考虑集合类型的优势.

所以Where(predicate).Single()能够做一些Single(predicate)没有的优化

  • 有趣的是,`Single()`对`IList`进行了类似的优化,其中`Single(谓词)`没有. (3认同)