Trova () vs. Where (). FirstOrDefault ()


161

Vedo spesso persone che usano Where.FirstOrDefault()fare una ricerca e afferrare il primo elemento. Perché non usare solo Find()? C'è un vantaggio per l'altro? Non saprei dire la differenza.

namespace LinqFindVsWhere
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> list = new List<string>();
            list.AddRange(new string[]
            {
                "item1",
                "item2",
                "item3",
                "item4"
            });

            string item2 = list.Find(x => x == "item2");
            Console.WriteLine(item2 == null ? "not found" : "found");
            string item3 = list.Where(x => x == "item3").FirstOrDefault();
            Console.WriteLine(item3 == null ? "not found" : "found");
            Console.ReadKey();
        }
    }
}

45
FWIW, list.FirstOrDefault(x => x == "item3");è più conciso rispetto all'uso di entrambi .Wheree .FirstOrDefault.
Kirk Woll,

@Kirk, immagino che la mia prossima domanda sia: perché hanno aggiunto la scoperta? Questo è un buon consiglio. L'unica cosa che mi viene in mente è che FirstOrDefault potrebbe restituire un valore predefinito diverso da null. Altrimenti sembra solo un'aggiunta inutile.
KingOfHypocrites,

8
Findprecede LINQ. (era disponibile in .NET 2.0 e non si poteva usare lambdas. Si era costretti a usare metodi normali o metodi anonimi)
Kirk Woll,

Risposte:


205

Dov'è il Findmetodo IEnumerable<T>? (Domanda retorica.)

La Wheree FirstOrDefaultmetodi sono applicabili contro più tipi di sequenze, tra cui List<T>, T[], Collection<T>, ecc Qualsiasi sequenza che implementa IEnumerable<T>possono utilizzare questi metodi. Findè disponibile solo per List<T>. I metodi che sono generalmente più applicabili, sono quindi più riutilizzabili e hanno un impatto maggiore.

Immagino che la mia prossima domanda sia: perché hanno aggiunto la scoperta? Questo è un buon consiglio. L'unica cosa che mi viene in mente è che FirstOrDefault potrebbe restituire un valore predefinito diverso da null. Altrimenti sembra solo un'aggiunta inutile

Findon List<T>precede gli altri metodi. List<T>è stato aggiunto con generici in .NET 2.0 e Findfaceva parte dell'API per quella classe. Wheree FirstOrDefaultsono stati aggiunti come metodi di estensione per IEnumerable<T>con Linq, che è una versione successiva di .NET. Non posso dire con certezza che se Linq esistesse con la versione 2.0 che Findnon sarebbe mai stata aggiunta, ma questo è probabilmente il caso di molte altre funzionalità fornite nelle versioni .NET precedenti che erano state rese obsolete o ridondanti dalle versioni successive.


85
Solo per completare: non è necessario chiamare Where e First o FirstOrDefault: First o FirstOrDefault consente di specificare un predicato di ricerca, rendendo superflua la chiamata Where
Robson Rocha,

4
Ma Where(condition).FirstOrDefault()ottimizza almeno anche a volte meglio che FirstOrDefault(condition)da solo. Usiamo sempre Where()per migliorare le prestazioni quando disponibili.
Suncat2000,

7
@ Suncat2000 fornisci un esempio, per favore
Konstantin Salavatov,

2
@ Suncat2000 Stai usando Linq per il suo potere espressivo e vuoi scrivere un codice dichiarativo. Non dovresti preoccuparti di tali micro miglioramenti, che possono anche cambiare in future implementazioni. Inoltre, non ottimizzare troppo presto
Piotr Falkowski

50

L'ho scoperto oggi, facendo alcuni test su un elenco di 80.000 oggetti e ho scoperto che Find()può essere fino al 1000% più veloce rispetto all'uso di un Wherecon FirstOrDefault(). Non lo sapevo fino a quando non ho provato un timer prima e dopo tutto. A volte era lo stesso tempo, altrimenti era più veloce.


6
Hai provato con Where AND FirstOrDefault? Se forse l'hai provato, prova solo con FirstOrDefault e vedi se Find () è ancora meglio.
MVCKarl,

5
Sembra che tu non abbia materializzato il risultato con .ToList()o .ToArray()per eseguire effettivamente la query.
Andrew Morton,

4
È perché Findutilizza le chiavi primarie (quindi gli indici), mentre Whereè una semplice query sql
percebus

4
A partire da EF6, Find e FirstOrDefault generano entrambi esattamente le stesse istruzioni SQL. Puoi vedere l'SQL in un'app console eseguendo context.Database.Log = Console.Write; L'esempio situato utilizza "Trova" in memoria su un elenco di stringhe, non su un DB con chiavi primarie. Forse la traduzione dell'istruzione della clausola Find - che omette la necessità di eseguire l'analisi delle espressioni lambda è la ragione del miglioramento delle prestazioni in questo caso. Contro un database dubito che noterai una differenza nelle situazioni RL ... Mi chiedo anche se è stato testato usando Trova prima invece di seconda ...
Elenco C.

2
Bene, questo miglioramento delle prestazioni è dovuto al fatto che find () controlla nella cache per l'oggetto prima di colpire il DB, mentre dove () sempre andare al DB per ottenere l'oggetto
Gaurav,

35

C'è una differenza molto importante se la fonte dei dati è Entity Framework: Findtroverà entità nello stato 'aggiunto' che non sono ancora persistite, ma Wherenon lo faranno. Questo è di progettazione.



1

oltre a Anthony, rispondi alla Where()visita attraverso tutti i record e poi restituisca i risultati mentre Find()non è necessario attraversare tutti i record se il predicato corrisponde a un determinato predicato.

quindi supponiamo di avere un elenco di classi Test con proprietà ide nameproprietà.

 List<Test> tests = new List<Test>();
 tests.Add(new Test() { Id = 1, Name = "name1" });
 tests.Add(new Test() { Id = 2, Name = "name2" });
 tests.Add(new Test() { Id = 3, Name = "name3" });
 tests.Add(new Test() { Id = 4, Name = "name2" }); 
 var r = tests.Find(p => p.Name == "name2");
 Console.WriteLine(r.Id);

Fornirà l'output di 2, e solo 2 visite Trova necessarie per dare risultato, ma se lo usi visiteremo Where().FirstOrDefault()tutti i record e quindi otterremo risultati.

Quindi, quando sai che vuoi solo il primo risultato dai record nella raccolta Find()sarà più adatto alloraWhere().FirtorDefault();


4
ma se usi Where (). FirstOrDefault () visiteremo tutti i record e quindi avremo i risultati. No. FirstOrDefault"riempie" la catena e smette di enumerare tutto. Uso il termine "bubble-up" per mancanza di un'espressione migliore, perché in realtà ogni selettore / predicato passerà al successivo, quindi l'ultimo metodo della catena sta effettivamente lavorando prima.
Silvermind,

1

Wow, guardo il tutorial EF di MicrosofToolbox oggi su Youtube. Ha detto di usare Find () e FirstOrDefault (condizione) nella query e Find () cercherà i dati che hai eseguito su quell'oggetto (aggiungi o modifica o elimina - ma non ancora salvati nel database) nel frattempo FirstOrDefault farà solo cerca ciò che è già stato salvato


-1

Find()è l'equivalente IEnumerable di a FirstOrDefault(). Non dovresti concatenare entrambi. Where () .FirstOrDefault()perché .Where()passa attraverso l'intero array e quindi eseguirà l'iterazione attraverso l'elenco per trovare il primo elemento. Risparmia una quantità incredibile di tempo inserendo il tuo predicato di ricerca nel FirstOrDefault()metodo.

Inoltre, ti incoraggio a leggere la domanda collegata a questo thread per saperne di più sulle migliori prestazioni sull'uso degli .Find()scenari specifici Performance of Find () vs. FirstOrDefault ()


Questo è un duplicato della risposta sopra la tua. Inoltre, vedi il commento di Silvermind su quella risposta.
carlin.scott,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.