在System.Linq
名称空间中,我们现在可以将IEnumerable扩展为具有Any()和Count() 扩展方法。
最近有人告诉我,如果我要检查集合中是否包含1个或多个项目,则应该使用.Any()
扩展方法而不是.Count() > 0
扩展方法,因为.Count()
扩展方法必须遍历所有项目。
其次,一些集合具有属性(未扩展方法),其是Count
或Length
。使用这些代替而不是.Any()
或会更好.Count()
?
是/否?
在System.Linq
名称空间中,我们现在可以将IEnumerable扩展为具有Any()和Count() 扩展方法。
最近有人告诉我,如果我要检查集合中是否包含1个或多个项目,则应该使用.Any()
扩展方法而不是.Count() > 0
扩展方法,因为.Count()
扩展方法必须遍历所有项目。
其次,一些集合具有属性(未扩展方法),其是Count
或Length
。使用这些代替而不是.Any()
或会更好.Count()
?
是/否?
Answers:
如果你开始的东西,有一个.Length
或.Count
(如ICollection<T>
,IList<T>
,List<T>
,等) -那么这将是最快的选择,因为它不需要去通过GetEnumerator()
/ MoveNext()
/ Dispose()
所要求的顺序Any()
,检查是否有非空IEnumerable<T>
序列。
对于刚刚IEnumerable<T>
,然后Any()
将通常更快,因为它只有看一次迭代。但是,请注意,的LINQ-to-Objects实现Count()
确实进行了检查ICollection<T>
(.Count
用作优化)-因此,如果您的基础数据源直接是列表/集合,则不会有很大的不同。不要问我为什么不使用非通用ICollection
...
当然,如果您使用LINQ对其进行过滤(Where
等等),则将具有基于迭代器块的序列,因此此ICollection<T>
优化是无用的。
通常与IEnumerable<T>
:坚持使用; Any()
-p
(somecollection.Count > 0)
?在引入LINQ的.Any()方法之前,我们所有的代码是否难以理解?
someCollection.Count > 0
很清晰,someCollection.Any()
并且具有更高的性能以及不需要LINQ的额外好处。当然,这是一个非常简单的情况,使用LINQ运算符的其他构造将使开发人员的意图比等效的非LINQ选项更加清晰。
注意:当实体框架4实际存在时,我写了这个答案。这个答案的要点是不要进入琐碎.Any()
VS .Count()
性能测试。关键是要表明EF远非完美。较新的版本会更好...但是,如果您的部分代码很慢并且使用EF,请使用直接TSQL进行测试并比较性能,而不要依赖假设(.Any()
总比更快.Count() > 0
)。
尽管我同意大多数投票赞成的答案和评论-特别是在点上Any
表明开发人员的意图要好于Count() > 0
-我遇到了这样的情况:在SQL Server上,Count的数量级更快(EntityFramework 4)。
这是带有Any
thew超时异常的查询(约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();
Count
以毫秒为单位执行的版本:
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'选择顶部(1) [Project2]。[ContactId] AS [ContactId], [Project2]。[CompanyId] AS [CompanyId], [Project2]。[ContactName] AS [ContactName], [Project2]。[FullName] AS [FullName], [Project2]。[ContactStatusId] AS [ContactStatusId], [Project2]。[创建] AS [创建] 从(选择[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] 从(选择 [Extent1]。[ContactId] AS [ContactId], [Extent1]。[CompanyId] AS [CompanyId], [Extent1]。[ContactName] AS [ContactName], [Extent1]。[FullName] AS [FullName], [Extent1]。[ContactStatusId] AS [ContactStatusId], [范围1]。[创建] AS [创建] 来自[dbo]。[联系方式]为[范围1] 在([Extent1]。[CompanyId] = @ p__linq__0)和([[Extent1]。[ContactStatusId] <= 3)和(不存在(选择 1 AS [C1] 从[dbo]。[NewsletterLog] AS [Extent2] 在([Extent1]。[ContactId] = [Extent2]。[ContactId])和(6 = [Extent2]。[NewsletterLogTypeId])中 )) )AS [Project2] )AS [Project2] 在[Project2]中。[行号]> 99 ORDER BY [Project2]。[ContactId] ASC',N'@ p__linq__0 int',@ p__linq__0 = 4
COUNT
:
exec sp_executesql N'选择顶部(1) [Project2]。[ContactId] AS [ContactId], [Project2]。[CompanyId] AS [CompanyId], [Project2]。[ContactName] AS [ContactName], [Project2]。[FullName] AS [FullName], [Project2]。[ContactStatusId] AS [ContactStatusId], [Project2]。[创建] AS [创建] 从(选择[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] 从(选择 [Project1]。[ContactId] AS [ContactId], [Project1]。[CompanyId] AS [CompanyId], [Project1]。[ContactName] AS [ContactName], [Project1]。[FullName] AS [FullName], [Project1]。[ContactStatusId] AS [ContactStatusId], [Project1]。[创建] AS [创建] 从(选择 [Extent1]。[ContactId] AS [ContactId], [Extent1]。[CompanyId] AS [CompanyId], [Extent1]。[ContactName] AS [ContactName], [Extent1]。[FullName] AS [FullName], [Extent1]。[ContactStatusId] AS [ContactStatusId], [Extent1]。[Created] AS [Created], (选择 COUNT(1)AS [A1] 从[dbo]。[NewsletterLog] AS [Extent2] 其中([Extent1]。[ContactId] = [Extent2]。[ContactId])AND(6 = [Extent2]。[NewsletterLogTypeId]))AS [C1] 来自[dbo]。[联系方式]为[范围1] )AS [Project1] 在([Project1]。[CompanyId] = @ p__linq__0)和([Project1]。[ContactStatusId] <= 3)和(0 = [Project1]。[C1]) )AS [Project2] )AS [Project2] 在[Project2]中。[行号]> 99 ORDER BY [Project2]。[ContactId] ASC',N'@ p__linq__0 int',@ p__linq__0 = 4
似乎使用EXISTS进行纯Where运算要比计算Count然后使用Count == 0进行Where运算要差得多。
让我知道你们是否发现我的发现有误。不管“任何与计数”的讨论如何,所有这些都可以得出的结论是,将更复杂的LINQ重写为存储过程时会更好;)。
由于这是一个非常受欢迎的话题,并且答案各不相同,因此我不得不重新审视这个问题。
测试环境: EF 6.1.3,SQL Server,30万条记录
桌子型号:
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()〜3毫秒
Count()〜230ms用于第一次查询,〜400ms用于第二次查询
备注:
就我而言,EF没有像他的帖子中提到的@Ben那样生成SQL。
Count() > 0
。:D
编辑:在EF版本6.1.1中已修复。而且这个答案不再实际
对于SQL Server和EF4-6,Count()的执行速度大约是Any()的两倍。
当您运行Table.Any()时,它会生成类似的内容(警报:不要伤害试图理解它的大脑)
SELECT
CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM [Table] AS [Extent1]
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT
1 AS [C1]
FROM [Table] AS [Extent2]
)) THEN cast(0 as bit) END AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
需要根据情况扫描2行。
我不喜欢写,Count() > 0
因为它掩盖了我的意图。我更喜欢使用自定义谓词:
public static class QueryExtensions
{
public static bool Exists<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate)
{
return source.Count(predicate) > 0;
}
}
这取决于数据集的大小以及您对性能的要求是什么?
如果没什么大不了的,请使用最易读的形式,对我自己来说是任何形式,因为它更短,更易读,而不是方程式。
关于计数()方法,如果IEnumarable是ICollection的,那么我们不能迭代的所有项目,因为我们可以检索计数的领域ICollection的,如果IEnumerable的是不是一个ICollection的,我们必须重复的所有项目使用,而用一个MoveNext,看看.NET Framework代码:
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;
}
参考:参考源可枚举
您可以进行简单的测试来弄清楚这一点:
var query = //make any query here
var timeCount = new Stopwatch();
timeCount.Start();
if (query.Count > 0)
{
}
timeCount.Stop();
var testCount = timeCount.Elapsed;
var timeAny = new Stopwatch();
timeAny.Start();
if (query.Any())
{
}
timeAny.Stop();
var testAny = timeAny.Elapsed;
检查testCount和testAny的值。
Count
Count()与.Any()方法代替,而不是属性。您需要迭代时间。
如果您使用的是Entity Framework,并且有一个包含许多记录的巨大表,那么Any()将会更快。我记得有一次我想检查一个表是否为空并且有数百万行。完成Count()> 0花费了20-30秒。Any()是瞬间的。
Any()可以提高性能,因为它可能不必迭代集合来获取事物的数量。它只需要打其中之一。或者,例如对于LINQ-to-Entities,生成的SQL将是IF EXISTS(...),而不是SELECT COUNT ...甚至SELECT *...。