LINQ Any vs FirstOrDefault的性能!= null

时间:2011-12-01 10:41:50

标签: c# linq

我提供的开源项目(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文章。

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

7 个答案:

答案 0 :(得分:48)

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

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

答案 1 :(得分:45)

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

http://msdn.microsoft.com/en-us/library/bb534972.aspx

我希望表现非常相似。请注意,FirstOrDefault版本不适用于值类型的集合(因为默认值不为null),但Any版本将为。

答案 2 :(得分:9)

这个问题的问题在于没有在上下文中询问。 我提供了一个答案,因为我在代码审查中看到了很多,这让我很烦恼。 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 ?

答案 3 :(得分:3)

为什么在找到满足条件的元素后才能继续?如果条件适用于1个元素,则该条件符合“任何”条件。

我认为他们都应该表现相同,但Any()确实更清楚地表达了你的意图。

答案 4 :(得分:1)

我的两分钱......

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

- 我有一个“人”表

-A“COMPANY”表

-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组件和我的模型的调用。

希望这有帮助......所以测试得好:),

答案 5 :(得分:1)

我们可以使用 .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]

答案 6 :(得分:0)

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