Performance of LINQ Any vs FirstOrDefault != null

本文关键字:null FirstOrDefault vs of LINQ Any Performance | 更新日期: 2023-09-27 18:16:24

在我贡献的开源项目(OSP)代码中有多个地方必须确定集合中的元素是否满足特定条件。

我看到LINQ表达式Any(lambda expression)在某些情况下使用,FirstOrDefault(lambda expression) != null在其他情况下使用,但从未考虑过它。

我现在已经达到了一个点,我必须做一些迭代从查询到DB的集合,并希望优化运行时。

所以我认为FirstOrDefault(lambda expression) != null应该比Any(lambda expression)快,对吗?

对于FirstOrDefault(lambda expression) != null,迭代(可能)在找到满足条件的元素时停止(最坏的情况是它遍历整个集合并返回null)。

Any(lambda expression)的情况下,即使找到满足条件的元素,也要继续迭代到集合的末尾。

编辑:以上是不正确的,Jackson Pope提到并链接了相关的MSDN文章。

我的想法是正确的还是我错过了什么?

Performance of LINQ Any vs FirstOrDefault != null

你把事情弄混了。你正在谈论集合,但你似乎不使用LINQ对象,但你正在查询数据库。

LINQ to objects:
Enumerable.AnyEnumerable.FirstOrDefault应该执行相同的操作,因为它们的代码几乎相同:

FirstOrDefault:

foreach (TSource source1 in source)
{
    if (predicate(source1))
        return source1;
}
return default (TSource);

Any:

foreach (TSource source1 in source)
{
    if (predicate(source1))
        return true
}
return false;

LINQ to some database:
您正在使用实体框架,LINQ to SQL或NHibernate,并在相应的数据上下文中使用Queryable.AnyQueryable.FirstOrDefault
在这种情况下,实际上没有集合,因为这些调用不是在内存对象上执行的,而是转换为SQL。

这意味着,性能差异源于LINQ提供程序如何将代码转换为SQL,因此最好是首先检查创建的语句。它们相等吗?或者它们是非常不同的(select count(0) from X vs. select top 1 from X)?那么差异可能在于数据库的查询优化器,索引和其他的…

Any()中的枚举在找到匹配项后立即停止:

https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.any

我希望性能非常相似。请注意,FirstOrDefault版本不能处理值类型的集合(因为默认值不是null),但Any版本可以。

这个问题的问题是它没有在上下文中被问到。我给出了一个答案,因为我在代码审查中经常看到这种情况,这让我很困扰。LINQ不应该成为停止思考的借口。

var people = new [] { "Steve", "Joe" };
if (people.Any(s => s == "Joe"))
{
    var joe = people.First(s => s == "Joe");
    // do something with joe
}
// if people is 1,000,000 people and joe is near the end do we want BigO to approach 2N here at worst case ?
var joe1N = people.FirstOrDefault(s => s == "Joe");
if (joe1N != null)
{
    // do something with joe
}
// or do we want to ensure worst case is N by simply using a variable ?

为什么要在找到满足条件的元素后继续呢?如果条件适用于一个元素,则该元素为"any"。

我认为它们都应该执行相同的操作,但是Any()确实更清楚地表达了您的意图。

My two cents…

我在Any()中遇到了巨大的性能问题。我使用teleerik网格来显示相关数据的列表,即

-I have a "PEOPLE" table

-A "COMPANY"表

-A "PEOPLE_COMPANY"链接表

-A "PEOPLE_ROL"链接表

和一个包含主类别、子类别和描述的"ROL"表。

这个视图混合了数据,并且有一些属性可以根据需要加载关于特定角色(admin, reporter, manager)的数据。

var x = GetPeople().Where(p => p.ROLs.Any(i => i.USO_Id == MainCatId && i.TUP_Id == (int)RolType) && p.PER_Id != per_id);
var x = GetPersonas().Where(p => p.ROLs.FirstOrDefault(i => i.USO_Id == MainCatId && i.TUP_Id == (int)RolType) != null && p.PER_Id != per_id);

我的网格使用AJAX,并需要超过10秒的加载使用"Any",和3或更少使用"FirstOrDefault"。没有花时间调试它,因为需要拦截来自teleerik组件和我的模型的调用。

希望这有助于…所以测试一下:)

我们可以使用。计数(x => x…) ! = 0而不是使用。任何(x => x…)或.FirstOrDefault (x => x…)! = null

因为Linq的查询生成在下面,

(在Any()中,我认为不需要第二个(Not Exists)条件)

。Any(x => x. col_1 == 'xxx')

    (@p__linq__0 varchar(8000))SELECT 
CASE WHEN ( EXISTS (SELECT 
    1 AS [C1]
    FROM [dbo].[Table_X] AS [Extent1]
    WHERE [Extent1].[Col_1] = @p__linq__0
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT 
    1 AS [C1]
    FROM [dbo].[Table_X] AS [Extent2]
    WHERE [Extent2].[Col_1] = @p__linq__0
)) THEN cast(0 as bit) END AS [C1]
FROM  ( SELECT 1 AS X ) AS [SingleRowTable1]

。FirstOrDefault (x => x.Col_1 = ="xxx")! = null

 (@p__linq__0 varchar(8000))SELECT TOP (1) 
[Extent1].[Col_1] AS [Col_1], 
[Extent1].[Col_2] AS [Col_2], 
...
[Extent1].[Col_n] AS [Col_n]
FROM [dbo].[Table_X] AS [Extent1]
WHERE [Extent1].[Col_1] = @p__linq__0

。Count(x => x. col_1 == 'xxx') != 0

  (@p__linq__0 varchar(8000))SELECT 
[GroupBy1].[A1] AS [C1]
FROM ( SELECT 
    COUNT(1) AS [A1]
    FROM [dbo].[Table_X] AS [Extent1]
    WHERE [Extent1].[Col_1] = @p__linq__0
)  AS [GroupBy1]

虽然它有类似的实现,但我相信Any要快一点,因为它不需要返回对象,在某些情况下,对象可能是丰富的,具有状态和行为,这取决于查询的考虑程度。