Find () и Where (). FirstOrDefault ()

161

Я часто вижу людей, использующих Where.FirstOrDefault()для поиска и захватить первый элемент. Почему бы просто не использовать Find()? Есть ли преимущество перед другим? Я не мог сказать разницу.

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
источник
45
FWIW, list.FirstOrDefault(x => x == "item3");является более кратким, чем использование обоих .Whereи .FirstOrDefault.
Кирк Волл
@ Кирк, я думаю, что мой следующий вопрос будет, почему они вообще добавили находку. Это хороший совет. Единственное, о чем я могу думать, это то, что FirstOrDefault мог бы вернуть другое значение по умолчанию, отличное от нуля. Иначе это только кажется бессмысленным дополнением.
KingOfHypocrites
8
Findпредшествует LINQ. (он был доступен в .NET 2.0, и вы не могли использовать лямбды. Вы были вынуждены использовать обычные методы или анонимные методы)
Кирк Волл

Ответы:

205

Где Findметод IEnumerable<T>? (Риторический вопрос.)

WhereИ FirstOrDefaultметоды применимы в отношении нескольких видов последовательностей, в том числе List<T>, T[], Collection<T>и т.д. Любой последовательности , которая реализует IEnumerable<T>могут использовать эти методы. Findдоступно только для List<T>. Методы, которые обычно более применимы, затем более пригодны для повторного использования и оказывают большее влияние.

Я думаю, мой следующий вопрос будет, почему они вообще добавили находку. Это хороший совет. Единственное, о чем я могу думать, это то, что FirstOrDefault мог бы вернуть другое значение по умолчанию, отличное от нуля. В противном случае это просто кажется бессмысленным дополнением

Findна List<T>предшествующих другие методы. List<T>был добавлен с обобщениями в .NET 2.0 и Findбыл частью API для этого класса. Whereи FirstOrDefaultбыли добавлены в качестве методов расширения для IEnumerable<T>Linq, более поздней версии .NET. Я не могу с уверенностью сказать, что если бы существовал Linq с выпуском 2.0, Findкоторый никогда не был бы добавлен, но это, возможно, относится ко многим другим функциям, которые были в более ранних версиях .NET, которые были сделаны устаревшими или избыточными в более поздних версиях.

Энтони Пеграм
источник
85
Просто для дополнения: нет необходимости вызывать Where и First или FirstOrDefault: либо First, либо FirstOrDefault позволяет указать предикат поиска, что делает ненужным вызов Where,
Робсон Роша
4
Но Where(condition).FirstOrDefault()оптимизирует хотя бы так же, а иногда и лучше, чем в FirstOrDefault(condition)одиночку. Мы всегда используем, Where()чтобы получить улучшенную производительность, когда она доступна.
Suncat2000
7
@ Suncat2000 приведи пример пожалуйста
Константин Салаватов
2
@ Suncat2000 Вы используете Linq из-за его выразительной силы и хотите написать декларативный код. Вы не должны беспокоиться о таких микро улучшениях, которые также могут измениться в будущих реализациях. Кроме того, не оптимизируйте слишком рано
Петр Фальковский
50

Я только что узнал сегодня, выполняя некоторые тесты со списком объектов размером 80 КБ, и обнаружил, что это Find()может быть до 1000% быстрее, чем с помощью Wherewith FirstOrDefault(). Я не знал этого до тестирования таймера до и после каждого из них. Иногда это было в то же время, в противном случае это было быстрее.

digiben
источник
6
Вы пробовали это с Где И FirstOrDefault? Если вы это сделали, возможно, попробуйте только с FirstOrDefault и посмотрите, будет ли Find () еще лучше.
MVCKarl
5
Похоже, что вы не материализовали результат с помощью .ToList()или, .ToArray()чтобы фактически выполнить запрос.
Эндрю Мортон
4
Это потому, Findчто использует первичные ключи (следовательно, индексы), в то Whereвремя как простой запрос sql
percebus
4
Начиная с EF6, Find и FirstOrDefault генерируют одинаковые операторы SQL. Вы можете увидеть SQL в консольном приложении, выполнив context.Database.Log = Console.Write; В приведенном примере используется «Поиск» в памяти для списка строк, а не для БД с первичными ключами. Возможно, перевод оператора в предложении Find, в котором нет необходимости разбирать лямбда-выражения, является причиной повышения производительности в этом случае. Относительно базы данных, я сомневаюсь, что вы заметите разницу в ситуациях с RL ... Мне также интересно, проверялось ли это с использованием Find сначала, а не вторым ...
C.List
2
Что ж, это улучшение производительности связано с тем, что find () проверяет в кеше объект перед тем, как попасть в БД, тогда как куда () всегда идти в БД, чтобы получить объект
Gaurav
35

Существует очень важное различие, если источником данных является Entity Framework: Findнайдет объекты в состоянии «добавлено», которые еще не сохранены, но Whereне будут. Это по замыслу.

меловой
источник
1

в дополнение к ответу Энтони, Where()просмотрите все записи и затем верните результат (ы), в то время как Find()не нужно просматривать все записи, если предикат совпадает с заданным предикатом.

скажем, у вас есть список idи nameсвойства класса Test .

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

Даст результат 2, и только 2 посещения. Найдите необходимые для получения результата, но если вы используете, Where().FirstOrDefault()мы будем посещать все записи, а затем мы получим результаты.

Таким образом, когда вы знаете, что хотите только первый результат из записей в коллекции, Find()будет более подходящим, чемWhere().FirtorDefault();

M Muneeb Ijaz
источник
4
но если вы используете Where (). FirstOrDefault (), мы будем посещать все записи, а затем получим результаты. Нет. FirstOrDefault«раздувает» цепь и перестанет все перечислять. Я использую термин «всплывающее» из-за отсутствия лучшего выражения, потому что фактически каждый селектор / предикат будет передан следующему, поэтому последний метод в цепочке фактически выполняет работу в первую очередь.
Серебряный
1

Ничего себе, я просто смотрю учебник EF от MicrosofToolbox сегодня на Youtube. Он говорил об использовании Find () и FirstOrDefault (условие) в запросе, а Find () будет искать данные, которые вы выполнили для этого объекта (добавить, изменить или удалить - но еще не сохранили в базе данных), в то время как FirstOrDefault будет только искать то, что уже было сохранено

Нгуен Хан
источник
-1

Find()является IEnumerable эквивалентом a FirstOrDefault(). Вы не должны связывать оба .Where () с, .FirstOrDefault()потому что он .Where()проходит через весь массив, а затем будет перебирать этот список, чтобы найти первый элемент. Вы экономите невероятное количество времени, помещая свой предикат поиска в FirstOrDefault()метод.

Кроме того, я рекомендую вам прочитать связанный вопрос с этой веткой, чтобы узнать больше о лучших показателях использования .Find()в конкретных сценариях производительности Find () по сравнению с FirstOrDefault ().

ремень кнута
источник
Это дубликат ответа выше вашего. Также см. Комментарий Silvermind к этому ответу.
carlin.scott