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.