Prestazioni di LINQ Any vs FirstOrDefault! = Null

Ci sono più posti in un codice OSP (Open Source Project) che contribuisco, dove deve essere determinato se un elemento in una collezione soddisfa una determinata condizione.

Ho visto l’uso dell’espressione LINQ Any(lambda expression) in alcuni casi e FirstOrDefault(lambda expression) != null negli altri, ma non ci ho mai pensato.

Ho raggiunto ora un punto in cui devo fare alcune iterazioni alle raccolte fatte da query a un DB e voglio ottimizzare il runtime.

Quindi ho capito che FirstOrDefault(lambda expression) != null dovrebbe essere più veloce di Any(lambda expression) , giusto?

Nel caso di FirstOrDefault(lambda expression) != null , l’iterazione (probabilmente) si interrompe quando trova un elemento che soddisfa la condizione (caso peggiore itera l’intera collezione e restituisce null ).

Nel caso di Any(lambda expression) immagino che l’iterazione continui fino alla fine della raccolta anche se viene trovato un elemento che soddisfa la condizione.

Modifica: Quanto sopra non è vero come Jackson Pope ha menzionato e collegato il relativo articolo MSDN.

I miei pensieri sono corretti o mi manca qualcosa?

L’enumerazione in Any() interrompe non appena trova un elemento corrispondente:

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

Mi aspetterei che la performance fosse molto simile. Si noti che la versione FirstOrDefault non funzionerà con una raccolta di tipi di valori (poiché il valore predefinito non è nullo) ma la versione Any farebbe.

Stai mescolando le cose qui. Stai parlando di collezioni, ma non ti sembra di usare LINQ per gli oggetti, ma stai interrogando un database.

LINQ agli oggetti:
Enumerable.Any e Enumerable.FirstOrDefault dovrebbero eseguire lo stesso, perché il loro codice è quasi identico:

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 qualche database:
Si sta utilizzando Entity Framework, LINQ to SQL o NHibernate e si utilizza Queryable.Any e Queryable.FirstOrDefault sul contesto dati corrispondente.
In questo caso, non ci sono realmente raccolte, perché queste chiamate non vengono eseguite negli oggetti di memoria ma tradotte in SQL.

Ciò significa che la differenza di prestazioni deriva dal modo in cui il provider LINQ traduce il codice in SQL, quindi il migliore sarebbe innanzitutto controllare le istruzioni create. Sono equivalenti? O sono molto diversi ( select count(0) from X vs. select top 1 from X )? La differenza potrebbe risiedere nel Query Optimizer del DB, indici e cosa no …

Il problema con questa domanda è che non viene chiesto nel contesto. Sto fornendo una risposta perché la vedo molto nelle recensioni di codice e mi dà fastidio. LINQ non dovrebbe essere una scusa per smettere di pensare.

 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 ? 

Perché dovrebbe continuare dopo aver trovato un elemento che soddisfa la condizione? Se la condizione si applica a 1 elemento, questo si qualifica come “qualsiasi”.

Penso che entrambi dovrebbero comportarsi allo stesso modo, ma Any () esprime più chiaramente la tua intenzione.

I miei due centesimi…

Ho avuto un enorme problema di prestazioni con Any (). Sto usando la griglia di Telerik per visualizzare un elenco di dati correlati, ad esempio

-Ho una tabella “PEOPLE”

-Una tabella “AZIENDA”

-Una tabella di link “PEOPLE_COMPANY”

-Una tabella di link “PEOPLE_ROL”

-E una tabella “ROL” con categoria principale, sottocategoria e descrizione.

La vista combina i dati e ha alcune proprietà che caricano i dati su richiesta su ruoli specifici (amministratore, 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); 

La mia griglia utilizza AJAX e impiega più di 10 secondi per caricare utilizzando “Qualsiasi”, e 3 o meno utilizzando “FirstOrDefault”. Non mi ero preso il tempo di eseguire il debugging come requieres per intercettare le chiamate dai componenti telerik e dal mio modello.

Spero che questo aiuti … quindi provalo bene 🙂

Possiamo usare .Count (x => x ….) ! = 0 invece di usare .Any (x => x ….) o .FirstOrDefault (x => x ….)! = Null

Poiché la generazione di query di Linq è inferiore,

(In Any (), la seconda condizione (non esiste) non è necessaria, credo.)

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