在系统中。Linq命名空间,我们现在可以扩展我们的IEnumerable来拥有Any()和Count()扩展方法。
最近有人告诉我,如果我想检查一个集合中是否包含1个或多个项目,我应该使用. any()扩展方法而不是. count() > 0扩展方法,因为. count()扩展方法必须遍历所有项目。
其次,一些集合具有Count或Length属性(而不是扩展方法)。使用这些,而不是.Any()或.Count()会更好吗?
-是的-不?
在系统中。Linq命名空间,我们现在可以扩展我们的IEnumerable来拥有Any()和Count()扩展方法。
最近有人告诉我,如果我想检查一个集合中是否包含1个或多个项目,我应该使用. any()扩展方法而不是. count() > 0扩展方法,因为. count()扩展方法必须遍历所有项目。
其次,一些集合具有Count或Length属性(而不是扩展方法)。使用这些,而不是.Any()或.Count()会更好吗?
-是的-不?
当前回答
具体细节在。net Framework和。net Core中略有不同,但这也在一定程度上取决于你在做什么:如果你正在使用ICollection或ICollection<T>类型(例如List<T>),则有一个. count属性易于访问,而其他类型可能需要枚举。
TL; diana:
如果属性存在,则使用. count > 0,否则使用. any()。
使用. count() > 0从来不是最好的选择,在某些情况下可能会非常慢。
这适用于。net Framework和。net Core。
现在我们可以开始讨论细节了。
列表和集合
让我们从一个非常常见的情况开始:使用List<T>(这也是ICollection<T>)。
.Count属性实现如下:
private int _size;
public int Count {
get {
Contract.Ensures(Contract.Result<int>() >= 0);
return _size;
}
}
这就是说,_size是由Add(),Remove()等维护的,因为它只是访问一个字段,这是一个非常便宜的操作——我们不需要迭代值。
ICollection和ICollection<T>都有. count,大多数实现它们的类型都可能以类似的方式这样做。
其他ienumerable
任何其他不是ICollection的IEnumerable类型都需要开始枚举来确定它们是否为空。影响性能的关键因素是我们最终是枚举单个项目(理想情况)还是整个集合(相对昂贵)。
如果收集实际上导致了I/O,比如从数据库或磁盘读取数据,这可能会对性能造成很大的影响。
.NET Framework .Any()
在.NET Framework(4.8)中,Any()实现是:
public static bool Any<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
using (IEnumerator<TSource> e = source.GetEnumerator()) {
if (e.MoveNext()) return true;
}
return false;
}
这意味着无论如何,它都将获得一个新的枚举器对象并尝试迭代一次。这比调用List<T>代价更大。属性,但至少它不是迭代整个列表。
.NET Framework .Count()
在.NET Framework(4.8)中,Count()实现(基本上)是:
public static int Count<TSource>(this IEnumerable<TSource> source)
{
ICollection<TSource> collection = source as ICollection<TSource>;
if (collection != null)
{
return collection.Count;
}
int num = 0;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num = checked(num + 1);
}
return num;
}
}
如果可用,ICollection。使用Count,但在其他情况下枚举集合。
.NET Core .Any()
. net Core中的LINQ Any()实现要聪明得多。你可以在这里看到完整的源代码,但与此讨论相关的部分:
public static bool Any<TSource>(this IEnumerable<TSource> source)
{
//..snip..
if (source is ICollection<TSource> collectionoft)
{
return collectionoft.Count != 0;
}
//..snip..
using (IEnumerator<TSource> e = source.GetEnumerator())
{
return e.MoveNext();
}
}
因为List<T>是一个ICollection<T>,这将调用Count属性(尽管它调用了另一个方法,但没有额外的分配)。
.NET Core .Count()
. net Core实现(源代码)基本上与. net Framework(见上文)相同,因此它将使用ICollection。如果可用则计数,否则枚举集合。
总结
net框架
ICollection: 计数> 0是最好的 . count() > 0很好,但最终只调用ICollection。数 .Any()会比较慢,因为它枚举单个项 使用非icollection (no .Count属性) .Any()是最好的,因为它只枚举单个项 .Count() > 0是坏的,因为它导致完全枚举
net核心
.Count > 0是最好的,如果可用(ICollection) . any()是很好的,将会做ICollection。计数> 0或枚举单个项目 .Count() > 0是坏的,因为它导致完全枚举
其他回答
具体细节在。net Framework和。net Core中略有不同,但这也在一定程度上取决于你在做什么:如果你正在使用ICollection或ICollection<T>类型(例如List<T>),则有一个. count属性易于访问,而其他类型可能需要枚举。
TL; diana:
如果属性存在,则使用. count > 0,否则使用. any()。
使用. count() > 0从来不是最好的选择,在某些情况下可能会非常慢。
这适用于。net Framework和。net Core。
现在我们可以开始讨论细节了。
列表和集合
让我们从一个非常常见的情况开始:使用List<T>(这也是ICollection<T>)。
.Count属性实现如下:
private int _size;
public int Count {
get {
Contract.Ensures(Contract.Result<int>() >= 0);
return _size;
}
}
这就是说,_size是由Add(),Remove()等维护的,因为它只是访问一个字段,这是一个非常便宜的操作——我们不需要迭代值。
ICollection和ICollection<T>都有. count,大多数实现它们的类型都可能以类似的方式这样做。
其他ienumerable
任何其他不是ICollection的IEnumerable类型都需要开始枚举来确定它们是否为空。影响性能的关键因素是我们最终是枚举单个项目(理想情况)还是整个集合(相对昂贵)。
如果收集实际上导致了I/O,比如从数据库或磁盘读取数据,这可能会对性能造成很大的影响。
.NET Framework .Any()
在.NET Framework(4.8)中,Any()实现是:
public static bool Any<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
using (IEnumerator<TSource> e = source.GetEnumerator()) {
if (e.MoveNext()) return true;
}
return false;
}
这意味着无论如何,它都将获得一个新的枚举器对象并尝试迭代一次。这比调用List<T>代价更大。属性,但至少它不是迭代整个列表。
.NET Framework .Count()
在.NET Framework(4.8)中,Count()实现(基本上)是:
public static int Count<TSource>(this IEnumerable<TSource> source)
{
ICollection<TSource> collection = source as ICollection<TSource>;
if (collection != null)
{
return collection.Count;
}
int num = 0;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num = checked(num + 1);
}
return num;
}
}
如果可用,ICollection。使用Count,但在其他情况下枚举集合。
.NET Core .Any()
. net Core中的LINQ Any()实现要聪明得多。你可以在这里看到完整的源代码,但与此讨论相关的部分:
public static bool Any<TSource>(this IEnumerable<TSource> source)
{
//..snip..
if (source is ICollection<TSource> collectionoft)
{
return collectionoft.Count != 0;
}
//..snip..
using (IEnumerator<TSource> e = source.GetEnumerator())
{
return e.MoveNext();
}
}
因为List<T>是一个ICollection<T>,这将调用Count属性(尽管它调用了另一个方法,但没有额外的分配)。
.NET Core .Count()
. net Core实现(源代码)基本上与. net Framework(见上文)相同,因此它将使用ICollection。如果可用则计数,否则枚举集合。
总结
net框架
ICollection: 计数> 0是最好的 . count() > 0很好,但最终只调用ICollection。数 .Any()会比较慢,因为它枚举单个项 使用非icollection (no .Count属性) .Any()是最好的,因为它只枚举单个项 .Count() > 0是坏的,因为它导致完全枚举
net核心
.Count > 0是最好的,如果可用(ICollection) . any()是很好的,将会做ICollection。计数> 0或枚举单个项目 .Count() > 0是坏的,因为它导致完全枚举
注意:我写这个答案时实体框架4是实际的。这个答案的重点并不是要进行琐碎的. any () vs . count()性能测试。重点是要表明EF远非完美。更新的版本更好…但是如果你有部分代码很慢,并且使用EF,请直接使用TSQL测试并比较性能,而不是依赖于假设(. any()总是比. count() > 0快)。
虽然我同意大多数赞成的答案和评论-特别是在这一点上,任何信号开发人员的意图都比Count() > 0 -我有过这样的情况,在SQL Server (EntityFramework 4)上,Count的速度要快一个数量级。
下面是使用Any查询的w超时异常(在~200.000条记录上):
con = db.Contacts.
Where(a => a.CompanyId == companyId && a.ContactStatusId <= (int) Const.ContactStatusEnum.Reactivated
&& !a.NewsletterLogs.Any(b => b.NewsletterLogTypeId == (int) Const.NewsletterLogTypeEnum.Unsubscr)
).OrderBy(a => a.ContactId).
Skip(position - 1).
Take(1).FirstOrDefault();
以毫秒为单位执行的计数版本:
con = db.Contacts.
Where(a => a.CompanyId == companyId && a.ContactStatusId <= (int) Const.ContactStatusEnum.Reactivated
&& a.NewsletterLogs.Count(b => b.NewsletterLogTypeId == (int) Const.NewsletterLogTypeEnum.Unsubscr) == 0
).OrderBy(a => a.ContactId).
Skip(position - 1).
Take(1).FirstOrDefault();
我需要找到一种方法来查看两个linq产生的确切SQL -但很明显,在某些情况下,Count和Any之间存在巨大的性能差异,不幸的是,似乎您不能在所有情况下都坚持使用Any。
编辑:这里是生成的sql。你可以看到的美;)
ANY:
exec sp_executesql N'SELECT TOP (1) [Project2].[ContactId] AS [ContactId], [Project2].[CompanyId] AS [CompanyId], [Project2].[ContactName] AS [ContactName], [Project2].[FullName] AS [FullName], [Project2].[ContactStatusId] AS [ContactStatusId], [Project2].[Created] AS [Created] FROM ( SELECT [Project2].[ContactId] AS [ContactId], [Project2].[CompanyId] AS [CompanyId], [Project2].[ContactName] AS [ContactName], [Project2].[FullName] AS [FullName], [Project2].[ContactStatusId] AS [ContactStatusId], [Project2].[Created] AS [Created], row_number() OVER (ORDER BY [Project2].[ContactId] ASC) AS [row_number] FROM ( SELECT [Extent1].[ContactId] AS [ContactId], [Extent1].[CompanyId] AS [CompanyId], [Extent1].[ContactName] AS [ContactName], [Extent1].[FullName] AS [FullName], [Extent1].[ContactStatusId] AS [ContactStatusId], [Extent1].[Created] AS [Created] FROM [dbo].[Contact] AS [Extent1] WHERE ([Extent1].[CompanyId] = @p__linq__0) AND ([Extent1].[ContactStatusId] <= 3) AND ( NOT EXISTS (SELECT 1 AS [C1] FROM [dbo].[NewsletterLog] AS [Extent2] WHERE ([Extent1].[ContactId] = [Extent2].[ContactId]) AND (6 = [Extent2].[NewsletterLogTypeId]) )) ) AS [Project2] ) AS [Project2] WHERE [Project2].[row_number] > 99 ORDER BY [Project2].[ContactId] ASC',N'@p__linq__0 int',@p__linq__0=4
数:
exec sp_executesql N'SELECT TOP (1) [Project2].[ContactId] AS [ContactId], [Project2].[CompanyId] AS [CompanyId], [Project2].[ContactName] AS [ContactName], [Project2].[FullName] AS [FullName], [Project2].[ContactStatusId] AS [ContactStatusId], [Project2].[Created] AS [Created] FROM ( SELECT [Project2].[ContactId] AS [ContactId], [Project2].[CompanyId] AS [CompanyId], [Project2].[ContactName] AS [ContactName], [Project2].[FullName] AS [FullName], [Project2].[ContactStatusId] AS [ContactStatusId], [Project2].[Created] AS [Created], row_number() OVER (ORDER BY [Project2].[ContactId] ASC) AS [row_number] FROM ( SELECT [Project1].[ContactId] AS [ContactId], [Project1].[CompanyId] AS [CompanyId], [Project1].[ContactName] AS [ContactName], [Project1].[FullName] AS [FullName], [Project1].[ContactStatusId] AS [ContactStatusId], [Project1].[Created] AS [Created] FROM ( SELECT [Extent1].[ContactId] AS [ContactId], [Extent1].[CompanyId] AS [CompanyId], [Extent1].[ContactName] AS [ContactName], [Extent1].[FullName] AS [FullName], [Extent1].[ContactStatusId] AS [ContactStatusId], [Extent1].[Created] AS [Created], (SELECT COUNT(1) AS [A1] FROM [dbo].[NewsletterLog] AS [Extent2] WHERE ([Extent1].[ContactId] = [Extent2].[ContactId]) AND (6 = [Extent2].[NewsletterLogTypeId])) AS [C1] FROM [dbo].[Contact] AS [Extent1] ) AS [Project1] WHERE ([Project1].[CompanyId] = @p__linq__0) AND ([Project1].[ContactStatusId] <= 3) AND (0 = [Project1].[C1]) ) AS [Project2] ) AS [Project2] WHERE [Project2].[row_number] > 99 ORDER BY [Project2].[ContactId] ASC',N'@p__linq__0 int',@p__linq__0=4
似乎纯粹的Where with EXISTS比计算Count然后用Count == 0执行Where要差得多。
如果你们发现我的发现有错误,请告诉我。不管Any vs Count的讨论如何,我们可以从所有这些中得出的结论是,任何更复杂的LINQ在重写为存储过程时都要好得多。
如果您正在使用实体框架,并且有一个包含许多记录的巨大表,Any()将会更快。我记得有一次,我想检查一个有数百万行的表是否为空。Count() > 0完成耗时20-30秒。Any()是即时的。
Any()可以提高性能,因为它可能不必迭代集合来获得事物的数量。只要击中其中一个就行。或者,对于LINQ-to-Entities,生成的SQL将是IF EXISTS(…)而不是SELECT COUNT…甚至选择* ....
关于Count()方法,如果IEnumerable是一个ICollection,那么我们不能遍历所有的项目,因为我们可以检索ICollection的Count字段,如果IEnumerable不是一个ICollection,我们必须使用一个MoveNext while遍历所有的项目,看看。net框架代码:
public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
throw Error.ArgumentNull("source");
ICollection<TSource> collectionoft = source as ICollection<TSource>;
if (collectionoft != null)
return collectionoft.Count;
ICollection collection = source as ICollection;
if (collection != null)
return collection.Count;
int count = 0;
using (IEnumerator<TSource> e = source.GetEnumerator())
{
checked
{
while (e.MoveNext()) count++;
}
}
return count;
}
参考:可枚举的参考源
我已经使用IList创建了一个示例应用程序,其中包含100个元素到100万个项目,以查看Count vs Any哪个是最好的。
Code
class Program
{
static void Main()
{
//Creating List of customers
IList<Customer> customers = new List<Customer>();
for (int i = 0; i <= 100; i++)
{
Customer customer = new Customer
{
CustomerId = i,
CustomerName = string.Format("Customer{0}", i)
};
customers.Add(customer);
}
//Measuring time with count
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
if (customers.Count > 0)
{
Console.WriteLine("Customer list is not empty with count");
}
stopWatch.Stop();
Console.WriteLine("Time consumed with count: {0}", stopWatch.Elapsed);
//Measuring time with any
stopWatch.Restart();
if (customers.Any())
{
Console.WriteLine("Customer list is not empty with any");
}
stopWatch.Stop();
Console.WriteLine("Time consumed with count: {0}", stopWatch.Elapsed);
Console.ReadLine();
}
}
public class Customer
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
}
结果:
任何都比数好。