【问题标题】:Is the Linq Count() faster or slower than List.Count or Array.Length?Linq Count() 是比 List.Count 或 Array.Length 快还是慢?
【发布时间】:2010-11-02 03:33:23
【问题描述】:

LINQ Count() 方法比List<>.CountArray.Length 快还是慢?

【问题讨论】:

  • 最简单的方法就是尝试一下。将两者都封装在对 StopWatch 上适当方法的调用中,执行几百万次,您就会知道。
  • 除非我们谈论的是一些非常大的集合,否则速度上不会有明显差异可能一文不值。只需使用更易于阅读/维护的任何一个即可。

标签: c# .net linq


【解决方案1】:

一般较慢。 LINQ 的计数一般是O(N) 操作,而List.CountArray.Length 都保证为O(1)

但是,在某些情况下,LINQ 会通过强制转换为某些接口类型(例如 IList<T>ICollection<T>)来对 IEnumerable<T> 参数进行特殊处理。然后它将使用该 Count 方法执行实际的Count() 操作。所以它会回到O(1)。但是您仍然需要支付转换和接口调用的少量开销。

【讨论】:

  • 我不确定,但我认为如果 List.Count() 在 IQueryable 上运行,它将执行 select count(*) sql 命令。但如果 List.Count 运行,它将枚举所有项目,然后返回计数。如果是后者,List.Count() 在大多数情况下会更快。
  • @Jared,Marcs 的回答更准确,检查只针对 ICollection、数组、HashSet、Dictionary、List、LinkedList 和 Queue 都实现了 ICollection。旧的 System.Collection 类没有,但无论如何也不实现 IEnumerable,因此它们不能与 LINQ 一起使用。
  • @sambo99,是的,我应该添加通用说明符(我很懒,没有想到这会如何影响我的答案)。不久添加。
  • 你可以在这里查看Enumerable.Count()算法:referencesource.microsoft.com/#System.Core/System/Linq/…
【解决方案2】:

Enumerable.Count() 方法检查 ICollection<T>,使用 .Count - 所以对于数组和列表,它的效率并没有低多少(只是额外的间接级别)。

【讨论】:

  • 实际上使用数组,您可以获得 2 层间接,请参阅我的答案:p
【解决方案3】:

马克有正确的答案,但魔鬼在细节中。

在我的机器上:

  • 对于数组,.Length 比 .Count() 快大约 100 倍
  • 对于列表,.Count 比 .Count() 快大约 10 倍 - 注意:我希望所有实现 IList<T> 的集合都有类似的性能

数组的启动速度较慢,因为 .Length 仅涉及单个操作,而 .Count 数组涉及一层间接。所以 .Count on arrays 开始慢 10 倍(在我的机器上),这可能是显式实现接口的原因之一。想象一下,如果您有一个具有两个公共属性 .Count 和 .Length 的对象。两者都做完全相同的事情,但 .Count 慢了 10 倍。

当然,这些都不会产生很大的不同,因为您必须每秒数百万次计算数组和列表才能感受到性能影响。

代码:

    static void TimeAction(string description, int times, Action func) {
        var watch = new Stopwatch();
        watch.Start();
        for (int i = 0; i < times; i++) {
            func();
        }
        watch.Stop();
        Console.Write(description);
        Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
    } 

    static void Main(string[] args) {
        var array = Enumerable.Range(0, 10000000).ToArray();
        var list = Enumerable.Range(0, 10000000).ToArray().ToList();

        // jit
        TimeAction("Ignore and jit", 1 ,() =>
        {
            var junk = array.Length;
            var junk2 = list.Count;
            array.Count();
            list.Count();
        });


        TimeAction("Array Length", 1000000, () => {
            var tmp1 = array.Length;
        });

        TimeAction("Array Count()", 1000000, () =>
        {
            var tmp2 = array.Count();
        });

        TimeAction("Array Length through cast", 1000000, () =>
        {
            var tmp3 = (array as ICollection<int>).Count;
        });


        TimeAction("List Count", 1000000, () =>
        {
            var tmp1 = list.Count;
        });

        TimeAction("List Count()", 1000000, () =>
        {
            var tmp2 = list.Count();
        });

        Console.ReadKey();
    }

结果:

Array Length Time Elapsed 3 ms Array Count() 已用时间 264 毫秒 阵列长度通过施放时间经过 16 毫秒 列表计数时间经过 3 毫秒 列表计数()经过的时间 18 毫秒

【讨论】:

  • 幸运的是 collection.Count/Lengthcollection.Count() 更具可读性。更漂亮的代码性能更高的罕见情况:P
  • 仅供参考,我发现(array as ICollection&lt;int&gt;).Count;(array as ICollection).Count; 之间存在细微差别(支持前者)。
【解决方案4】:

我相信,如果您在 ICollection 或 IList(如 ArrayList 或 List)上调用 Linq.Count(),那么它只会返回 Count 属性的值。因此,在普通集合上的性能将大致相同。

【讨论】:

  • ArrayList 不是 IEnumerable 所以无论如何你都不能在它上面执行 LINQ 扩展方法。仅对 ICollection 进行检查
【解决方案5】:

我会说这取决于列表。如果它是一个 IQueryable,它是某个数据库中的一个表,那么 Count() 将快得多,因为它不必加载所有对象。但是如果列表是在内存中的,我猜如果不是差不多的话,Count 属性会更快。

【讨论】:

    【解决方案6】:

    一些附加信息 - LINQ 计数 - 使用它与不使用它之间的差异可能很大 - 这也不必超过“大”集合。我有一个从 linq 到包含大约 6500 个项目的对象的集合(大......但无论如何都不大)。 Count() 在我的情况下需要几秒钟。转换为列表(或数组,无论如何)计数实际上是立即的。将这个计数放在内部循环中意味着影响可能是巨大的。 Count 枚举所有内容。数组和列表都“自我感知”它们的长度,不需要枚举它们。任何引用此 count() 的调试语句(例如 log4net )也会大大减慢一切。帮自己一个忙,如果您需要引用它,请经常保存计数大小,并且只在 LINQ 集合上调用一次,除非您将其转换为列表,然后可以引用而不会影响性能。

    这是对我上面所说的内容的快速测试。请注意,每次我们调用 Count() 时,我们的集合大小都会发生变化。因此会发生评估,这超出了预期的“计数”操作。只是需要注意的事情:)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace LinqTest { class TestClass { public TestClass() { CreateDate = DateTime.Now; } public DateTime CreateDate; } class Program { static void Main(string[] args) { //Populate the test class List list = new List(1000); for (int i=0; i<1000; i++) { System.Threading.Thread.Sleep(20); list.Add(new TestClass()); if(i%100==0) { Console.WriteLine(i.ToString() + " items added"); } } //now query for items var newList = list.Where(o=> o.CreateDate.AddSeconds(5)> DateTime.Now); while (newList.Count() > 0) { //Note - are actual count keeps decreasing.. showing our 'execute' is running every time we call count. Console.WriteLine(newList.Count()); System.Threading.Thread.Sleep(500); } } } }

    【讨论】:

    • list.Where 返回一个 IEnumerable 所以你没有使用 Count() 的快捷方式......如果你实现它,你会看到相当不错的性能(例如:list.Where(o=&gt; o.CreateDate.AddSeconds(5)&gt; DateTime.Now).ToList()
    【解决方案7】:

    List.CountArray.Length 确实比 Linq Count() 快。因为 Linq Count() 将遍历要计数的整个项目列表。 List.CountArray.Length 使用他们的财产。

    【讨论】:

      猜你喜欢
      • 2011-09-25
      • 1970-01-01
      • 1970-01-01
      • 2011-12-18
      • 2010-11-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-07
      相关资源
      最近更新 更多