IEnumerable.Count()或ToList().Count

Paw*_*zyk 8 c# linq performance

我得到了我自己的类的对象列表,看起来像:

public class IFFundTypeFilter_ib
{
    public string FundKey { get; set; }
    public string FundValue { get; set; }
    public bool IsDisabled { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

IsDisabled通过查询collection.Where(some condition)和计算匹配对象的数量来设置属性.结果是IEnumarable<IFFundTypeFilter_ib>不包含属性Count.我想知道,什么会更快.

这个:

collection.Where(somecondition).Count();
Run Code Online (Sandbox Code Playgroud)

或者这个:

collection.Where(someocondition).ToList().Count;
Run Code Online (Sandbox Code Playgroud)

集合可能包含很少的对象,但也可以包含,例如700.我将进行两次计数调用和其他条件.在第一个条件中,我检查FundKey是否等于某个键,在第二个条件中我做同样的事情,但我将它与其他键值进行比较.

Eni*_*ity 15

您询问:

我想知道,什么会更快.

每当你要求你真正计时并发现它时.

我开始测试获得计数的所有这些变体:

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(),
};
Run Code Online (Sandbox Code Playgroud)

我的测试代码显式地运行每个方法1,000次,用a测量每个执行时间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")
        });
Run Code Online (Sandbox Code Playgroud)

以下是结果(从最慢到最快排序):

+---------+-----------------------------------------------------------+
| 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(),                                       |
+---------+-----------------------------------------------------------+
Run Code Online (Sandbox Code Playgroud)

这里有两件很重要的事情.

一,任何带.ToList()内联的方法都明显慢于没有它的等效方法.

二,LINQ运算符在可能的情况下利用可枚举的基础类型来进行快捷计算.该enumerable.Count()list.Count()方法显示这一点.

list.Countlist.Count()电话之间没有区别.所以关键比较是在enumerable.Where(x => x % 2 == 0).Count()enumerable.Where(x => x % 2 == 0).ToList().Count()调用之间.由于后者包含额外的操作,我们预计它需要更长的时间.它差不多2.5毫秒.

我不知道为什么你说你要两次调用计数代码,但如果你这样做,最好建立一个列表.如果不是只是.Count()在查询后进行普通调用.