关于c:linq count()比list.count或array.length快还是慢?

Is the Linq Count() faster or slower than List.Count or Array.Length?

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


一般来说,速度较慢。Linq的计数一般是一个O(N)操作,而List.CountArray.Length都保证是O(1)操作。

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


Enumerable.Count()方法使用.Count检查ICollection,因此在数组和列表的情况下,它的效率并不高(只是一个额外的间接级别)。


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

在我的机器上:

  • 对于数组。长度大约比.count()快100倍
  • 对于lists.count比.count()快10倍-注意:我希望实现IList的所有集合都有类似的性能。

数组的开始速度较慢,因为.length只涉及一个操作,.count on array涉及一个间接层。所以,数组的计数开始时慢了10倍(在我的机器上),这可能是接口显式实现的原因之一。想象一下,如果有一个对象有两个公共属性,.count和.length。两个都做同样的事情,但是计数慢了10倍。

当然,如果不这样做,确实会有很大的不同,因为您必须计算数组并每秒列出数百万次才能感觉到性能受到影响。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
    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();
    }

结果:

1
2
3
4
5
Array Length Time Elapsed 3 ms
Array Count() Time Elapsed 264 ms
Array Length through cast Time Elapsed 16 ms
List Count Time Elapsed 3 ms
List Count() Time Elapsed 18 ms


我认为这取决于清单。如果它是一个IQueryable,它是数据库中某个地方的一个表,那么count()将更快,因为它不需要加载所有的对象。但是,如果列表在内存中,我想Count属性会更快,如果不是相同的话。


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


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


一些额外的信息——linq计数——使用它和不使用它之间的区别可能很大——而且这也不一定要超过"大"集合。我有一个从LINQ到对象的集合,大约有6500个项目(大……但无论如何都不是很大)。在我的例子中,count()需要几秒钟。转换成一个列表(或数组,whatver),计数实际上是即时的。在一个内部循环中使用这个计数意味着影响可能很大。计数枚举所有内容。数组和列表都是对其长度的"自我感知",不需要枚举它们。引用此count()的任何调试语句(log4net for ex)也会大大降低速度。帮你自己一个忙,如果你需要引用它,通常会保存计数大小,并且在一个LINQ集合中只调用它一次,除非你将它转换为一个列表,然后可以引用掉而不影响性能。

这是我刚才所说的一个快速测试。注意,每次调用count()时,集合大小都会发生变化。因此会发生评估,这超出了预期的"count"操作。只是需要注意的事情:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
    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);
                }
            }
        }
    }
</wyn>