Find () vs. Where (). FirstOrDefault ()

161

Ich sehe oft Leute Where.FirstOrDefault(), die eine Suche durchführen und das erste Element greifen. Warum nicht einfach benutzen Find()? Gibt es einen Vorteil für den anderen? Ich konnte keinen Unterschied feststellen.

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();
        }
    }
}
KingOfHypocrites
quelle
45
FWIW list.FirstOrDefault(x => x == "item3");ist prägnanter als die Verwendung von .Whereund .FirstOrDefault.
Kirk Woll
@ Kirk, ich denke meine nächste Frage wäre, warum sie den Fund überhaupt hinzugefügt haben. Das ist ein guter Tipp. Ich kann mir nur vorstellen, dass FirstOrDefault einen anderen Standardwert als null zurückgeben könnte. Ansonsten scheint es nur eine sinnlose Ergänzung zu sein.
KingOfHypocrites
8
Findälter als LINQ. (Es war in .NET 2.0 verfügbar und Sie konnten keine Lambdas verwenden. Sie mussten normale oder anonyme Methoden verwenden.)
Kirk Woll

Antworten:

204

Wo ist die FindMethode IEnumerable<T>? (Rhetorische Frage.)

Die Whereund FirstOrDefaultVerfahren ist anwendbar gegen mehrere Arten von Sequenzen, einschließlich List<T>, T[], Collection<T>etc. beliebiger Reihenfolge , dass Arbeitsgeräte IEnumerable<T>diese Methoden verwenden kann. Findist nur für die verfügbar List<T>. Methoden, die im Allgemeinen besser anwendbar sind, sind dann wiederverwendbarer und haben eine größere Wirkung.

Ich denke, meine nächste Frage wäre, warum sie den Fund überhaupt hinzugefügt haben. Das ist ein guter Tipp. Ich kann mir nur vorstellen, dass FirstOrDefault einen anderen Standardwert als null zurückgeben könnte. Ansonsten scheint es nur eine sinnlose Ergänzung zu sein

Findon ist List<T>älter als die anderen Methoden. List<T>wurde mit Generika in .NET 2.0 hinzugefügt und Findwar Teil der API für diese Klasse. Whereund FirstOrDefaultwurden als Erweiterungsmethoden für IEnumerable<T>Linq hinzugefügt , eine spätere .NET-Version. Ich kann nicht mit Sicherheit sagen, dass wenn Linq mit der Version 2.0 existiert Findhätte , diese niemals hinzugefügt worden wäre, aber dies ist wohl der Fall für viele andere Funktionen, die in früheren .NET-Versionen enthalten waren, die durch spätere Versionen veraltet oder überflüssig wurden.

Anthony Pegram
quelle
85
Nur zur Ergänzung: Es ist nicht erforderlich, Where and First oder FirstOrDefault aufzurufen: Mit First oder FirstOrDefault können Sie ein Suchprädikat angeben, sodass der Where-Aufruf nicht erforderlich ist
Robson Rocha
4
Aber Where(condition).FirstOrDefault()optimiert mindestens so gut und manchmal besser als FirstOrDefault(condition)allein. Wir verwenden immer Where(), um die verbesserte Leistung zu erhalten, wenn verfügbar.
Suncat2000
7
@ Suncat2000 geben Sie bitte ein Beispiel
Konstantin Salavatov
2
@ Suncat2000 Sie verwenden Linq aufgrund seiner Ausdruckskraft und möchten deklarativen Code schreiben. Sie sollten sich nicht mit solchen Mikroverbesserungen befassen, die sich auch in zukünftigen Implementierungen ändern können. Optimieren Sie auch nicht zu früh
Piotr Falkowski
50

Ich habe es heute gerade herausgefunden, als ich einige Tests mit einer Liste von 80K-Objekten durchgeführt habe, und festgestellt, dass Find()dies bis zu 1000% schneller sein kann als die Verwendung eines Wherewith FirstOrDefault(). Das wusste ich erst, als ich vor und nach jedem einen Timer getestet hatte. Manchmal war es die gleiche Zeit, sonst war es schneller.

digiben
quelle
6
Haben Sie es mit Where AND FirstOrDefault versucht? Wenn Sie es vielleicht nur mit FirstOrDefault versucht haben und sehen, ob Find () noch besser ist.
MVCKarl
5
Das klingt so, als hätten Sie das Ergebnis nicht mit einem .ToList()oder materialisiert .ToArray(), um die Abfrage tatsächlich durchzuführen.
Andrew Morton
4
Dies liegt daran, Finddass die Primärschlüssel (daher Indizes) verwendet werden, während Wherees sich um eine einfache SQL-Abfrage handelt
Percebus
4
Ab EF6 generieren Find und FirstOrDefault genau dieselben SQL-Anweisungen. Sie können SQL in einer Konsolen-App anzeigen, indem Sie context.Database.Log = Console.Write; Im Beispiel wird die speicherinterne "Suche" für eine Liste von Zeichenfolgen verwendet, nicht für eine Datenbank mit Primärschlüsseln. Vielleicht ist die Anweisungsübersetzung der Find-Klausel, bei der keine Lambda-Ausdrucksanalyse durchgeführt werden muss, der Grund für die Leistungsverbesserung in diesem Fall. Gegen eine Datenbank bezweifle ich, dass Sie einen Unterschied in RL-Situationen bemerken werden ... Ich frage mich auch, ob sie mit Find first statt second getestet wurde ...
C.List
2
Nun, diese Leistungsverbesserung ist darauf zurückzuführen, dass find () im Cache nach Objekten sucht, bevor DB getroffen wird, während where () immer in die DB geht, um das Objekt zu erhalten
Gaurav
35

Es gibt einen sehr wichtigen Unterschied, wenn die Datenquelle Entity Framework ist: FindEntitäten werden im Status 'hinzugefügt' gefunden, die noch nicht beibehalten wurden, aber Wherenicht. Dies ist beabsichtigt.

Kreidig
quelle
1

Zusätzlich zu Anthony beantworte den Where()Besuch durch alle Datensätze und Find()gib dann die Ergebnisse zurück, während nicht alle Datensätze durchlaufen werden müssen, wenn das Prädikat mit dem angegebenen Prädikat übereinstimmt.

Angenommen, Sie haben eine Liste mit Testklassen idund nameEigenschaften.

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

Gibt eine Ausgabe von 2und nur 2 Besuche. Die Suche wird benötigt, um das Ergebnis zu liefern. Wenn Sie jedoch verwenden Where().FirstOrDefault(), besuchen wir alle Datensätze und erhalten dann Ergebnisse.

Wenn Sie also wissen, dass Sie nur das erste Ergebnis von Datensätzen in der Sammlung Find()wünschen, ist dies besser geeignetWhere().FirtorDefault();

M Muneeb Ijaz
quelle
4
Wenn Sie jedoch Where (). FirstOrDefault () verwenden, besuchen wir alle Datensätze und erhalten dann Ergebnisse. Nee. FirstOrDefaultwird die Kette "sprudeln" lassen und aufhören, alles aufzuzählen. Ich verwende den Begriff "Bubble-up", weil es keinen besseren Ausdruck gibt, da tatsächlich jeder Selektor / jedes Prädikat an das nächste weitergegeben wird, sodass die letzte Methode in der Kette tatsächlich zuerst arbeitet.
Silvermind
1

Wow, ich schaue mir heute das EF-Tutorial von MicrosofToolbox auf Youtube an. Er hat über die Verwendung von Find () und FirstOrDefault (Bedingung) in der Abfrage gesprochen, und Find () sucht nach den Daten, die Sie für dieses Objekt ausgeführt haben (Hinzufügen oder Bearbeiten oder Löschen - aber noch nicht in der Datenbank gespeichert), während FirstOrDefault dies nur tut Suchen Sie nach dem, was bereits gespeichert wurde

Nguyen Khanh
quelle
-1

Find()ist das IEnumerable-Äquivalent von a FirstOrDefault(). Sie sollten nicht beide .Where () mit verketten, .FirstOrDefault()da das .Where()das gesamte Array durchläuft und dann diese Liste durchläuft, um das erste Element zu finden. Sie sparen unglaublich viel Zeit, indem Sie Ihr Suchprädikat in die FirstOrDefault()Methode einfügen.

Ich empfehle Ihnen außerdem, die mit diesem Thread verknüpfte Frage zu lesen, um mehr über die besseren Leistungen bei der Verwendung der .Find()in bestimmten Szenarien durchgeführten Leistung von Find () vs. FirstOrDefault () zu erfahren.

Schleudertrauma
quelle
Dies ist ein Duplikat der obigen Antwort. Siehe auch Silverminds Kommentar zu dieser Antwort.
carlin.scott