你问:
我想知道,什么会更快。
每当你问你应该实际计时并找出答案时。
我开始测试所有这些获取计数的变体:
var enumerable = Enumerable.Range(0, 1000000);
var list = enumerable.ToList();
var methods = new Func<int>[]
{
() => list.Count,
() => enumerable.Count(),
() => list.Count(),
() => enumerable.ToList().Count(),
() => list.ToList().Count(),
() => enumerable.Select(x => x).Count(),
() => list.Select(x => x).Count(),
() => enumerable.Select(x => x).ToList().Count(),
() => list.Select(x => x).ToList().Count(),
() => enumerable.Where(x => x % 2 == 0).Count(),
() => list.Where(x => x % 2 == 0).Count(),
() => enumerable.Where(x => x % 2 == 0).ToList().Count(),
() => list.Where(x => x % 2 == 0).ToList().Count(),
};
我的测试代码显式地运行每个方法 1000 次,使用 Stopwatch 测量每个执行时间,并忽略所有发生垃圾回收的结果。然后它会获得每个方法的平均执行时间。
var measurements =
methods
.Select((m, i) => i)
.ToDictionary(i => i, i => new List<double>());
for (var run = 0; run < 1000; run++)
{
for (var i = 0; i < methods.Length; i++)
{
var sw = Stopwatch.StartNew();
var gccc0 = GC.CollectionCount(0);
var r = methods[i]();
var gccc1 = GC.CollectionCount(0);
sw.Stop();
if (gccc1 == gccc0)
{
measurements[i].Add(sw.Elapsed.TotalMilliseconds);
}
}
}
var results =
measurements
.Select(x => new
{
index = x.Key,
count = x.Value.Count(),
average = x.Value.Average().ToString("0.000")
});
以下是结果(从最慢到最快排序):
+---------+-----------------------------------------------------------+
| average | method |
+---------+-----------------------------------------------------------+
| 14.879 | () => enumerable.Select(x => x).ToList().Count(), |
| 14.188 | () => list.Select(x => x).ToList().Count(), |
| 10.849 | () => enumerable.Where(x => x % 2 == 0).ToList().Count(), |
| 10.080 | () => enumerable.ToList().Count(), |
| 9.562 | () => enumerable.Select(x => x).Count(), |
| 8.799 | () => list.Where(x => x % 2 == 0).ToList().Count(), |
| 8.350 | () => enumerable.Where(x => x % 2 == 0).Count(), |
| 8.046 | () => list.Select(x => x).Count(), |
| 5.910 | () => list.Where(x => x % 2 == 0).Count(), |
| 4.085 | () => enumerable.Count(), |
| 1.133 | () => list.ToList().Count(), |
| 0.000 | () => list.Count, |
| 0.000 | () => list.Count(), |
+---------+-----------------------------------------------------------+
这里有两件事很重要。
第一,任何带有 .ToList() 内联的方法都比没有它的等效方法慢得多。
第二,LINQ 运算符尽可能利用可枚举的底层类型来简化计算。 enumerable.Count() 和 list.Count() 方法显示了这一点。
list.Count 和 list.Count() 调用之间没有区别。所以关键的比较是在enumerable.Where(x => x % 2 == 0).Count() 和enumerable.Where(x => x % 2 == 0).ToList().Count() 调用之间。由于后者包含一个额外的操作,我们预计它需要更长的时间。几乎长了 2.5 毫秒。
我不知道您为什么说要调用两次计数代码,但如果您这样做,最好构建列表。如果不只是在查询后进行普通的.Count() 调用。