在系统中。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()会更好吗?
-是的-不?
当前回答
使用Count()来测试是否为空,但使用Any()可以使意图更清晰,代码更可读。然而,有一些情况需要特别注意:
如果集合是一个EntityFramework或其他ORM查询,调用Count()将导致执行一个潜在的大规模SQL查询,并可能给应用程序数据库带来很大的开销。调用Any()也将连接到数据库,但将生成更高效的SQL。
如果集合是包含创建对象的Select()语句的LINQ查询的一部分,则可能会不必要地分配大量内存。调用Any()将更有效,因为它将执行更少的枚举迭代。
使用Any()的示例:
private static bool IsEmpty(IEnumerable<string> strings)
{
return !strings.Any();
}
其他回答
如果您正在使用实体框架,并且有一个包含许多记录的巨大表,Any()将会更快。我记得有一次,我想检查一个有数百万行的表是否为空。Count() > 0完成耗时20-30秒。Any()是即时的。
Any()可以提高性能,因为它可能不必迭代集合来获得事物的数量。只要击中其中一个就行。或者,对于LINQ-to-Entities,生成的SQL将是IF EXISTS(…)而不是SELECT COUNT…甚至选择* ....
注意:我写这个答案时实体框架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在重写为存储过程时都要好得多。
如果您从具有. length或. count的东西开始(例如ICollection<T>, IList<T>, List<T>,等等),那么这将是最快的选项,因为它不需要通过Any()所要求的GetEnumerator()/MoveNext()/Dispose()序列来检查非空IEnumerable<T>序列。
对于IEnumerable<T>,则Any()通常会更快,因为它只需要查看一次迭代。但是,请注意Count()的LINQ-to-Objects实现确实检查ICollection<T>(使用.Count作为优化)-因此,如果您的底层数据源直接是一个列表/集合,则不会有很大的区别。不要问我为什么不使用非泛型的ICollection…
当然,如果你已经使用LINQ来过滤它(Where等),你将有一个基于迭代器块的序列,所以这个ICollection<T>优化是无用的。
通常,对于IEnumerable<T>:坚持使用Any();-p
由于这是一个相当流行的话题,答案各不相同,我不得不重新审视这个问题。
测试env: EF 6.1.3, SQL Server, 300k记录
表模型:
class TestTable
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
}
测试代码:
class Program
{
static void Main()
{
using (var context = new TestContext())
{
context.Database.Log = Console.WriteLine;
context.TestTables.Where(x => x.Surname.Contains("Surname")).Any(x => x.Id > 1000);
context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Any(x => x.Id > 1000);
context.TestTables.Where(x => x.Surname.Contains("Surname")).Count(x => x.Id > 1000);
context.TestTables.Where(x => x.Surname.Contains("Surname") && x.Name.Contains("Name")).Count(x => x.Id > 1000);
Console.ReadLine();
}
}
}
结果:
Any() ~ 3ms
Count()第一次查询~ 230ms,第二次查询~ 400ms
备注:
对于我的案例,EF没有像@Ben在他的帖子中提到的那样生成SQL。
使用Count()来测试是否为空,但使用Any()可以使意图更清晰,代码更可读。然而,有一些情况需要特别注意:
如果集合是一个EntityFramework或其他ORM查询,调用Count()将导致执行一个潜在的大规模SQL查询,并可能给应用程序数据库带来很大的开销。调用Any()也将连接到数据库,但将生成更高效的SQL。
如果集合是包含创建对象的Select()语句的LINQ查询的一部分,则可能会不必要地分配大量内存。调用Any()将更有效,因为它将执行更少的枚举迭代。
使用Any()的示例:
private static bool IsEmpty(IEnumerable<string> strings)
{
return !strings.Any();
}