关于c#:LINQ的性能Any vs 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)的情况下,我设想迭代将继续到集合的末尾,即使找到了满足条件的元素。

编辑:正如杰克逊·波普提到并链接了相关的msdn文章,上述说法是不正确的。

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


你在这里混东西。您谈论的是集合,但似乎没有使用Linq to对象,而是在查询数据库。

Linq到对象:Enumerable.AnyEnumerable.FirstOrDefault应该执行相同的操作,因为它们的代码几乎相同:

FirstOrDefault

1
2
3
4
5
6
foreach (TSource source1 in source)
{
    if (predicate(source1))
        return source1;
}
return default (TSource);

Any

1
2
3
4
5
6
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?那么,区别可能在于数据库的查询优化器、索引以及其他什么…


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

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

我希望演出非常相似。注意,FirstOrDefault版本不适用于值类型集合(因为默认值不为空),但Any版本可以。


这个问题的问题是它不是在上下文中被问到的。我提供了一个答案,因为我在代码审查中看到了很多,这让我很困扰。Linq不应该成为停止思考的借口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 ?


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

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


我们可以使用.count(x=>x…)!=0而不是使用.any(x=>x…)或.first或default(x=>x…)!=空

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

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

.any(x=>x.col_1='xxx')

1
2
3
4
5
6
7
8
9
10
11
    (@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')!=空

1
2
3
4
5
6
7
 (@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

1
2
3
4
5
6
7
  (@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]


我的两分钱…

我对任何()都有很大的性能问题。我正在使用Telerik网格显示相关数据的列表,即,

-我有一张"人"桌

-"公司"表

-"人民公司"链接表

-"人物角色"链接表

-以及"rol"表,其中包含主类别、子类别和说明。

该视图混合了数据,并具有一些属性,这些属性按需加载有关特定角色(管理员、报告者、管理者)的数据。

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

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