C#:空列表的Any()vs Count()
早些时候发布的一条消息让我思考。在空列表中使用时,C#:空列表的Any()vs Count(),c#,.net,C#,.net,早些时候发布的一条消息让我思考。在空列表中使用时,Any()和Count()是否会执行类似的操作 如前所述,两者都应该经历相同的步骤GetEnumerator()/MoveNext()/Dispose() 我在LINQPad上使用quick程序对此进行了测试: static void Main() { var list = new List<int>(); Stopwatch stopwatch = new Stopwatch(); stopwatch.S
Any()
和Count()
是否会执行类似的操作
如前所述,两者都应该经历相同的步骤GetEnumerator()/MoveNext()/Dispose()
我在LINQPad上使用quick程序对此进行了测试:
static void Main()
{
var list = new List<int>();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 10000; i++)
list.Any();
stopwatch.Stop();
Console.WriteLine("Time elapsed for Any() : {0}", stopwatch.Elapsed);
stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 10000; i++)
list.Count();
stopwatch.Stop();
Console.WriteLine("Time elapsed for Count(): {0}", stopwatch.Elapsed);
}
static void Main()
{
var list=新列表();
秒表秒表=新秒表();
秒表。开始();
对于(int i=0;i<10000;i++)
list.Any();
秒表;
WriteLine(“Any()经过的时间:{0}”,stopwatch.appeased);
秒表=新秒表();
秒表。开始();
对于(int i=0;i<10000;i++)
list.Count();
秒表;
WriteLine(“Count()经过的时间:{0}”,stopwatch.appeased);
}
一般结果似乎表明,Count()
在这种情况下速度更快。为什么呢
我不确定我是否得到了正确的基准,如果没有,我将不胜感激
编辑:我理解它在语义上更有意义。我在问题中发布的第一个链接显示了一种情况,即直接使用
Count()
是有意义的,因为将使用值,因此问题就出现了。方法针对ICollection
类型进行了优化,因此不使用模式GetEnumerator()/MoveNext()/Dispose()
list.Count();
翻译成
((ICollection)list).Count;
而Any()
必须构建一个枚举器。
因此Count()
方法更快
这里是4个不同的IEnumerable
实例的基准测试。MyEmpty
看起来像IEnumerable MyEmpty(){yield break;}
iterations : 100000000
Function Any() Count()
new List<int>() 4.310 2.252
Enumerable.Empty<int>() 3.623 6.975
new int[0] 3.960 7.036
MyEmpty<int>() 5.631 7.194
你的基准测试到底显示了什么?我认为只要调用这其中的任何一个10000次都会非常快,以至于无法进行合理的测量。一般来说,
Any
更好的原因是,它只需要在枚举中找到一个东西,而count需要找到所有的东西。在您的测试中,列表是空的,因此很明显,查找第一个列表和查找所有列表没有多大区别请注意,Count()的LINQ to Objects实现确实检查ICollection(使用.Count作为优化)-因此,如果您的基础数据源直接是列表/集合,实际上,您已经链接到了回答此问题的问题,这与
没有太大区别。正如上面提到的,在我看来,最好选择语义上更准确的方法(除非你是在CPU限制的关键路径上,但这是非常罕见的)。虽然理论上很有趣,但我认为这个问题没有什么价值:如果你已经知道列表是空的,你不需要调用任何一个方法;如果您不知道,您应该更喜欢Any
而不是Count
。使用Enumerable.Empty()
表明Any()
比Count()快。谢谢-1:你的测试不正确Enumerable.Empty()
返回一个空数组,数组实现扩展ICollection
的IList
。您需要一种只做产生中断
的方法。该调用实质上是在测试中嗅出相同的代码路径。
class Program
{
public const long Iterations = (long)1e8;
static void Main()
{
var results = new Dictionary<string, Tuple<TimeSpan, TimeSpan>>();
results.Add("new List<int>()", Benchmark(new List<int>(), Iterations));
results.Add("Enumerable.Empty<int>()", Benchmark(Enumerable.Empty<int>(), Iterations));
results.Add("new int[0]", Benchmark(new int[0], Iterations));
results.Add("MyEmpty<int>()", Benchmark(MyEmpty<int>(), Iterations));
Console.WriteLine("Function".PadRight(30) + "Any()".PadRight(10) + "Count()");
foreach (var result in results)
{
Console.WriteLine("{0}{1}{2}", result.Key.PadRight(30), Math.Round(result.Value.Item1.TotalSeconds, 3).ToString().PadRight(10), Math.Round(result.Value.Item2.TotalSeconds, 3));
}
Console.ReadLine();
}
public static Tuple<TimeSpan, TimeSpan> Benchmark(IEnumerable<int> source, long iterations)
{
var anyWatch = new Stopwatch();
anyWatch.Start();
for (long i = 0; i < iterations; i++) source.Any();
anyWatch.Stop();
var countWatch = new Stopwatch();
countWatch.Start();
for (long i = 0; i < iterations; i++) source.Count();
countWatch.Stop();
return new Tuple<TimeSpan, TimeSpan>(anyWatch.Elapsed, countWatch.Elapsed);
}
public static IEnumerable<T> MyEmpty<T>() { yield break; }
}