Rendimiento de LINQ Any vs FirstOrDefault! = Null

Hay varios lugares en un código de proyecto de código abierto (OSP) que contribuyo, donde debe determinarse si un elemento en una colección satisface una determinada condición.

He visto el uso de la expresión LINQ Any(lambda expression) en algunos casos y FirstOrDefault(lambda expression) != null en otros, pero nunca he pensado en ello.

Llegué a un punto en el que tengo que hacer algunas iteraciones a las colecciones realizadas desde consultas a un DB y quiero optimizar el tiempo de ejecución.

Así que pensé que FirstOrDefault(lambda expression) != null debería ser más rápido que Any(lambda expression) , ¿verdad?

En el caso de FirstOrDefault(lambda expression) != null , la iteración (probablemente) se detiene cuando encuentra un elemento que satisface la condición (en el peor de los casos, itera por toda la colección y devuelve null ).

En el caso de Any(lambda expression) me imagino que la iteración continúa hasta el final de la colección incluso si se encuentra un elemento que satisface la condición.

Editar: Lo anterior no es cierto ya que Jackson Pope mencionó y enlazó el artículo relacionado de MSDN.

¿Son correctos mis pensamientos o me estoy perdiendo algo?

La enumeración en Any() detiene tan pronto como encuentra un elemento coincidente:

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

Esperaría que el rendimiento sea muy similar. Tenga en cuenta que la versión FirstOrDefault no funcionará con una colección de tipos de valores (ya que el valor predeterminado no es nulo), pero la versión Any haría.

Estás mezclando cosas aquí. Está hablando de colecciones, pero parece que no usa LINQ para objetos, pero está consultando una base de datos.

LINQ a los objetos:
Enumerable.Any y Enumerable.FirstOrDefault deben realizar lo mismo, porque su código es casi idéntico:

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 a alguna base de datos:
Está utilizando Entity Framework, LINQ to SQL o NHibernate y utiliza Queryable.Any y Queryable.FirstOrDefault en el contexto de datos correspondiente.
En este caso, realmente no hay colecciones, porque estas llamadas no se ejecutan en objetos de memoria sino que se traducen a SQL.

Esto significa que la diferencia de rendimiento se debe a la forma en que el proveedor de LINQ traduce el código a SQL, por lo que lo mejor sería verificar primero las declaraciones creadas. ¿Son equivalentes? ¿O son muy diferentes ( select count(0) from X vs. select top 1 from X )? Que la diferencia podría estar en el optimizador de consultas de la base de datos, los índices y lo que no …

El problema con esta pregunta es que no se formula dentro del contexto. Estoy proporcionando una respuesta porque veo esto mucho en las revisiones de código y me molesta. LINQ no debería ser una excusa para dejar de pensar.

 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 ? 

¿Por qué debería continuar después de que haya encontrado un elemento que satisfaga la condición? Si la condición se aplica a 1 elemento, eso califica como “cualquiera”.

Creo que ambos deberían tener el mismo rendimiento, pero Any () expresa tu intención más claramente.

Mis dos centavos…

Tuve un gran problema de rendimiento con Any (). Estoy usando Telerik grid para mostrar una lista de datos relacionados, es decir,

-Tengo una mesa de “PERSONAS”

– Una mesa “COMPAÑÍA”

– Una tabla de enlaces “PEOPLE_COMPANY”

– Una tabla de enlaces “PEOPLE_ROL”

-Y una tabla “ROL” con categoría principal, subcategoría y descripción.

La vista mezcla datos y tiene algunas propiedades que cargan datos según demanda sobre roles específicos (administrador, reportero, administrador).

 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); 

Mi cuadrícula usa AJAX, y toma más de 10 segundos para cargar usando “Cualquiera”, y 3 o menos usando “FirstOrDefault”. No me había tomado el tiempo para depurarlo, ya que requiere interceptar llamadas de componentes telerik y mi modelo.

Espero que esto ayude … así que pruébalo bien 🙂

Podemos usar .Count (x => x ….) ! = 0 en lugar de usar .Any (x => x ….) o .FirstOrDefault (x => x ….)! = Null

Porque la generación de consulta de Linq está debajo,

(En Any (), la segunda condición (no existe) no es necesaria, creo).

.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]