我得到了我自己班级的对象列表,如下所示:
public class IFFundTypeFilter_ib
{
public string FundKey { get; set; }
public string FundValue { get; set; }
public bool IsDisabled { get; set; }
}
通过执行查询IsDisabled
并计算匹配对象的数量来设置属性collection.Where(some condition)
。结果是IEnumarable<IFFundTypeFilter_ib>
,其中不包含属性Count。我想知道,什么会更快。
这一个:
collection.Where(somecondition).Count();
或者这个:
collection.Where(someocondition).ToList().Count;
Collection可能包含很少的对象,但也可以包含,例如700.我将进行两次计数调用以及其他条件。在第一个条件中,我检查FundKey是否等于某个键,在第二个条件中我做同样的事情,但我将它与其他键值进行比较。
答案 0 :(得分:12)
你问:
我想知道,什么会更快。
每当你要求你真正计时并发现时。
我打算测试所有获得计数的变体:
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(),
};
我的测试代码显式运行每个方法1,000次,用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()
调用。
答案 1 :(得分:4)
通常,实现列表的效率会降低。
此外,如果您使用两个条件,则无法缓存结果或将查询具体化为List
。
你应该只使用接受谓词的Count
重载:
collection.Count(someocondition);
正如@CodeCaster在评论中提到的那样,它等同于collection.Where(condition).Count()
,但更具可读性和简洁性。
答案 2 :(得分:3)
完全按照这种方式使用
var count = collection.Where(somecondition).ToList().Count;
没有意义 - 填充列表只是为了得到计数,所以使用IEnumerable<T>.Count()
是适用于这种情况的方法。
在你做这样的事情的情况下使用ToList
会有意义
var list = collection.Where(somecondition).ToList();
var count = list.Count;
// do something else with the list