繁体   English   中英

LINQ Any 与 FirstOrDefault 的性能 != null

[英]Performance of LINQ Any vs FirstOrDefault != null

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

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

我现在已经到了必须对从查询到数据库所做的集合进行一些迭代并希望优化运行时的地步。

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

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

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

编辑:上述内容与 Jackson Pope 提到并链接了相关的 MSDN 文章时不一样。

我的想法是正确的还是我遗漏了什么?

你在这里混杂。 您在谈论集合,但您似乎没有使用 LINQ to objects,但您正在查询数据库。

LINQ 到对象:
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 到某个数据库:
您正在使用实体框架、LINQ to SQL 或 NHibernate,并在相应的数据上下文中使用Queryable.AnyQueryable.FirstOrDefault
在这种情况下,实际上没有集合,因为这些调用不是在内存对象上执行,而是转换为 SQL。

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

Any()的枚举一旦找到匹配项就会停止:

https://docs.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 在找到满足条件的元素后还要继续? 如果条件适用于 1 个元素,则符合“任何”的条件。

我认为它们的表现应该差不多,但是 Any() 确实更清楚地表达了您的意图。

我的两分钱...

Any() 有一个巨大的性能问题。 我正在使用 Telerik 网格来显示相关数据列表,即,

-我有一张“人”表

-“公司”表

-A "PEOPLE_COMPANY" 链接表

-A "PEOPLE_ROL" 链接表

- 以及带有主类别、子类别和描述的“ROL”表。

该视图混合数据并具有一些属性,可根据需要加载有关特定角色(管理员、记者、经理)的数据。

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,使用“Any”加载需要 10 多秒,使用“FirstOrDefault”加载 3 秒或更少。 没有花时间调试它,因为需要拦截来自 Telerik 组件和我的模型的调用。

希望这会有所帮助...所以好好测试一下:)

我们可以使用.Count(x => x....) != 0 而不是使用.Any(x => x....) 或 .FirstOrDefault(x => x....) != null

因为 Linq 的查询生成如下,

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

.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 更快一点,因为它不必返回一个对象,在某些情况下,对象可能很丰富,具有状态和行为,取决于查询的考虑程度。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM