Linq Count()比List.Count或Array.Length更快还是更慢?
Linq Count(
)方法是否比List<>.Count
或Array.Length
更快或更慢?
一般来说比较慢。 一般情况下,LINQ的Count是一个O(N)
操作,而List.Count
和Array.Length
都保证是O(1)
。
但是,有些情况下,LINQ会通过强制转换为某些接口types(如IList<T>
或ICollection<T>
来特殊处理IEnumerable<T>
参数。 然后它将使用该Count方法来执行实际的Count()
操作。 所以它会回落到O(1)
。 但是你仍然要为演员和接口调用付出很小的开销。
Enumerable.Count()
方法使用.Count
检查ICollection<T>
– 所以在数组和列表的情况下,效率并不是太低(只是额外的间接级别)。
马克有正确的答案,但魔鬼是在细节。
在我的机器上:
- 对于数组,.Length大约比.Count()快100倍
- 对于列表.Count比.Count()快大约10倍 – 注意:我期望所有集合实现
IList<T>
数组起点较慢,因为.Length只涉及一个单独的操作,。数组上的Count包含一个间接层。 所以.Count在数组上开始慢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(); }
结果:
数组长度时间已用3 ms arrays计数()时间经过264毫秒 arrays长度通过施放时间经过16毫秒 列表计数时间经过了3毫秒 列表计数()时间已用18毫秒
我相信,如果您调用ICollection或IList(如ArrayList或List)上的Linq.Count(),那么它将只返回Count属性的值。 所以在普通collections上的performance大致相同。
我会说这取决于列表。 如果它是一个数据库中某个表的IQueryable,那么Count()会更快,因为它不必加载所有的对象。 但是,如果列表是在内存中,我猜测如果不相同,Count属性会更快。
一些额外的信息 – LINQ计数 – 使用它与不使用它们之间的差异可能是巨大的 – 这也不一定要超过“大”集合。 我有一个由linq形成的对象约6500个项目(大..但没有任何意义的巨大)的集合。 计数()在我的情况下需要几秒钟。 转换为一个列表(或数组,无论如何)计数是几乎立即。 有了这个计数在内部循环意味着影响可能是巨大的。 伯爵列举了一切。 数组和列表都是“自我意识”的长度,不需要枚举它们。 引用这个count()的任何debugging语句(对于ex的log4net)也会使得所有的事情变得更慢。 帮你一个忙,如果你需要经常引用这个保存计数大小,并只在LINQ集合调用一次,除非你将其转换为列表,然后可以参考而没有性能影响。
这是我刚才在谈论的一个快速testing。 每次我们调用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); } } } }
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); } } } }