Znajdź () a gdzie (). FirstOrDefault ()

161

Często widzę, jak ludzie Where.FirstOrDefault()wyszukują i chwytają pierwszy element. Dlaczego po prostu nie użyć Find()? Czy druga strona ma przewagę? Nie potrafiłem odróżnić.

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
źródło
45
FWIW, list.FirstOrDefault(x => x == "item3");jest bardziej zwięzły niż użycie obu .Wherei .FirstOrDefault.
Kirk Woll,
@Kirk, myślę, że moje następne pytanie brzmiałoby, dlaczego w ogóle dodali znalezisko. To dobra wskazówka. Jedyne, o czym przychodzi mi do głowy, to to, że FirstOrDefault może zwrócić inną wartość domyślną inną niż null. W przeciwnym razie wydaje się to po prostu bezcelowym dodatkiem.
KingOfHypocrites
8
Findpoprzedza LINQ. (był dostępny w .NET 2.0 i nie można było używać lambd. Zmuszono Cię do używania normalnych metod lub metod anonimowych)
Kirk Woll

Odpowiedzi:

205

Gdzie jest Findmetoda IEnumerable<T>? (Pytanie retoryczne.)

WhereI FirstOrDefaultsposoby są stosowane wobec wielu gatunków, w tym sekwencje List<T>, T[], Collection<T>itp dowolnej sekwencji narzędzia IEnumerable<T>może używać tych metod. Findjest dostępny tylko dla List<T>. Sposoby, które są stosowane bardziej powszechnie, to wówczas bardziej wielokrotnego użytku i ma większy wpływ.

Myślę, że moim następnym pytaniem byłoby, dlaczego w ogóle dodali znalezisko. To dobra wskazówka. Jedyne, o czym przychodzi mi do głowy, to to, że FirstOrDefault może zwrócić inną wartość domyślną inną niż null. W przeciwnym razie wydaje się to po prostu bezcelowym dodatkiem

Findna List<T>wyprzedza inne metody. List<T>został dodany z typami ogólnymi w .NET 2.0 i Findbył częścią interfejsu API dla tej klasy. Wherei FirstOrDefaultzostały dodane jako metody rozszerzające dla IEnumerable<T>Linq, który jest późniejszą wersją .NET. Nie mogę powiedzieć z całą pewnością, że gdyby Linq istniał w wydaniu 2.0, Findktóry nigdy nie zostałby dodany, ale prawdopodobnie tak jest w przypadku wielu innych funkcji, które pojawiły się we wcześniejszych wersjach .NET, które stały się przestarzałe lub zbędne w późniejszych wersjach.

Anthony Pegram
źródło
85
Tylko do uzupełnienia: nie ma potrzeby wywoływania Where and First lub FirstOrDefault: albo First, albo FirstOrDefault pozwala określić predykat wyszukiwania, dzięki czemu wywołanie Where jest niepotrzebne
Robson Rocha
4
Ale Where(condition).FirstOrDefault()optymalizuje przynajmniej równie dobrze, a czasem lepiej niż FirstOrDefault(condition)samodzielnie. Zawsze staramy Where()się uzyskać lepszą wydajność, gdy jest dostępna.
Suncat2000
7
@ Suncat2000 proszę podać przykład
Konstantin Salavatov
2
@ Suncat2000 Używasz Linqa ze względu na jego moc ekspresji i chcesz pisać kod deklaratywny. Nie powinieneś przejmować się takimi mikro ulepszeniami, które również mogą ulec zmianie w przyszłych wdrożeniach. Nie optymalizuj też zbyt wcześnie
Piotr Falkowski
50

Właśnie dowiedziałem się dzisiaj, robiąc kilka testów na liście 80 000 obiektów i stwierdziłem, że Find()może to być nawet o 1000% szybsze niż użycie Wherez FirstOrDefault(). Nie wiedziałem o tym, dopóki nie przetestowałem timera przed i po każdym. Czasami był to ten sam czas, w przeciwnym razie szybciej.

digiben
źródło
6
Czy próbowałeś tego z Where AND FirstOrDefault? Jeśli spróbowałeś, być może tylko z FirstOrDefault i zobacz, czy Find () jest nadal lepszy.
MVCKarl
5
Wygląda na to, że nie zmaterializowałeś wyniku za pomocą .ToList()lub .ToArray()faktycznie wykonałeś zapytanie.
Andrew Morton,
4
Dzieje się tak, ponieważ Findkorzysta z kluczy podstawowych (stąd indeksów), podczas gdy Wherejest to zwykłe zapytanie sql
virtubus
4
Począwszy od EF6, Find i FirstOrDefault generują dokładnie te same instrukcje SQL. Możesz zobaczyć kod SQL w aplikacji konsoli, wykonując context.Database.Log = Console.Write; Umieszczony przykład używa funkcji „Find” w pamięci do listy ciągów, a nie do bazy danych z kluczami podstawowymi. Być może tłumaczenie instrukcji klauzuli Find, która pomija potrzebę wykonywania analizy wyrażenia lambda, jest przyczyną poprawy wydajności w tym przypadku. W przypadku bazy danych wątpię, czy zauważysz różnicę w sytuacjach RL ... Zastanawiam się też, czy zostało to przetestowane przy użyciu funkcji Znajdź pierwszy zamiast drugiego ...
C.List
2
Cóż, ta poprawa wydajności wynika z tego, że find () sprawdza pamięć podręczną dla obiektu przed uderzeniem w DB, podczas gdy gdzie () zawsze przechodzi do DB, aby pobrać obiekt
Gaurav
35

Istnieje bardzo ważna różnica, jeśli źródłem danych jest Entity Framework: Findznajdzie jednostki w stanie „dodanym”, które nie zostały jeszcze utrwalone, ale Wheretak się nie stanie. Jest to zgodne z projektem.

Kredowy
źródło
1

oprócz Anthony'ego odpowiedz na Where()wizytę we wszystkich rekordach, a następnie zwróć wynik (y) bez Find()konieczności przechodzenia przez wszystkie rekordy, jeśli predykat pasuje do podanego predykatu.

więc powiedz, że masz List of Test klasy posiadającej idi namewłaściwości.

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

Daje wyjście 2i tylko 2 wizyty Znajdź potrzebne do podania wyniku, ale jeśli używaszWhere().FirstOrDefault() , odwiedzimy wszystkie rekordy, a następnie otrzymamy wyniki.

Tak więc, gdy wiesz, że chcesz, aby tylko pierwszy wynik z rekordów w kolekcji Find()był bardziej odpowiedniWhere().FirtorDefault();

M Muneeb Ijaz
źródło
4
ale jeśli użyjesz Where (). FirstOrDefault () odwiedzimy wszystkie rekordy i wtedy otrzymamy wyniki. Nie. FirstOrDefault„rozpali się” łańcuch i przestanie wyliczać wszystko. Używam terminu „bubble-up” z powodu braku lepszego wyrażenia, ponieważ w rzeczywistości każdy selektor / predykat zostanie przekazany do następnego, więc ostatnia metoda w łańcuchu faktycznie wykonuje pracę jako pierwsza.
Silvermind
1

Wow, po prostu oglądam dzisiaj samouczek EF z MicrosofToolbox na Youtube. Powiedział o używaniu Find () i FirstOrDefault (warunek) w zapytaniu, a Find () wyszuka dane, które zrobiłeś na tym obiekcie (dodałeś, edytujesz lub usuniesz - ale nie zostały jeszcze zapisane w bazie danych), tymczasem FirstOrDefault będzie tylko szukaj tego, co już zostało zapisane

nguyen khanh
źródło
-1

Find()jest IEnumerable odpowiednikiem a FirstOrDefault(). Nie powinieneś łączyć obu .Where () z, .FirstOrDefault()ponieważ .Where()przechodzi przez całą tablicę, a następnie będzie iterować po tej liście, aby znaleźć pierwszy element. Możesz zaoszczędzić niesamowitą ilość czasu, umieszczając predykat wyszukiwania w FirstOrDefault()metodzie.

Zachęcam również do przeczytania połączonego pytania do tego wątku, aby dowiedzieć się więcej o lepszych wynikach używania .Find()w określonych scenariuszach Wydajność Find () vs. FirstOrDefault ()

Bicz
źródło
To jest duplikat odpowiedzi powyżej Twojej. Zobacz także komentarz Silverminda dotyczący tej odpowiedzi.
carlin.scott