LINQ的Count()
方法和 List<>.Count
或者 Array.Length
哪个更快或更慢?
一般来说比较慢。LINQ的Count方法通常是一个O(N)
的操作,然而List.Count
和Array.Length
都保证是O(1)
的。
但在某些情况下,LINQ会特殊处理IEnumerable<T>
参数,将其转换为某些接口类型,例如IList<T>
或ICollection<T>
。 然后它将使用该Count方法执行实际的Count()
操作。 因此,它将返回O(1)
。 但您仍需要支付进行转换和接口调用的微小开销。
Enumerable.Count()
算法:https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,41ef9e39e54d0d0b - CreakEnumerable.Count()
方法检查 ICollection<T>
,使用 .Count
- 所以对于数组和列表而言,并没有太多的效率损失(只是额外增加了一层间接性)。
Marc的答案是正确的,但魔鬼在于细节。
在我的机器上:
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 毫秒 数组 Count() 时间消耗 264 毫秒 通过转换获得数组长度 时间消耗 16 毫秒 列表 Count 时间消耗 3 毫秒 列表 Count() 时间消耗 18 毫秒
collection.Count/Length
比collection.Count()
更易读。很少有情况下漂亮的代码会更高效 :P - nawfal(array as ICollection<int>).Count;
和(array as ICollection).Count;
之间有一些细微的差别(前者更好)。 - jmoreno我认为这取决于列表的类型。如果它是一个IQueryable,是数据库中的表格,那么Count()会快得多,因为它不需要加载所有对象。但如果列表在内存中,我猜想Count属性应该会更快,如果不是同样的话。
我认为,如果你在 ICollection 或 IList(如 ArrayList 或 List)上调用 Linq.Count(),它将只返回 Count 属性的值。因此,在普通集合上的性能将大致相同。
一些额外的信息 - LINQ Count - 使用它和不使用它之间的差异可能是巨大的 - 而且这并不一定是针对“大”集合。我有一个由linq to objects形成的集合,其中约有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)
{
//填充测试类
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() + " 个项目已添加");
}
}
//查询项目
var newList = list.Where(o=> o.CreateDate.AddSeconds(5)> DateTime.Now);
while (newList.Count() > 0)
{
//注意 - 实际计数不断减少...显示我们的“执行”每次调用计数时都在运行。
Console.WriteLine(newList.Count());
System.Threading.Thread.Sleep(500);
}
}
}
}
list.Where(o=> o.CreateDate.AddSeconds(5)> DateTime.Now).ToList()
)。 - Sam Saffron