Найти () против перечисления в списках

Я работаю с базой кода, где в списках нужно часто искать один элемент.

Быстрее ли использовать Predicate и Find (), чем вручную выполнять перечисление в списке?

Например:

string needle = "example";
FooObj result = _list.Find(delegate(FooObj foo) {
    return foo.Name == needle;
});

против.

string needle = "example";
foreach (FooObj foo in _list)
{
    if (foo.Name == needle)
        return foo;
}

Хотя они эквивалентны по функциональности, эквивалентны ли они и по производительности?

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
0
3 372
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Они не равноценны по производительности. Метод Find () требует вызова метода (в данном случае делегата) для каждого элемента в списке. Вызов метода платный и стоит относительно дорого по сравнению со встроенным сравнением. Версия foreach не требует дополнительного вызова метода для каждого объекта.

При этом я бы не выбрал тот или другой в зависимости от производительности, пока я не профилировал свой код и не обнаружил, что это проблема. Я еще не обнаружил, что накладные расходы этого сценария представляют собой проблему «горячего пути» для кода, который я написал, и я часто использую этот шаблон с Find и другими подобными методами.

У вас есть данные для подтверждения вашего иска? Как уже упоминал Далле, тесты показывают обратное для очень похожего кода. Я не вижу причин, по которым компилятор в принципе не должен иметь возможность встроить лямбду, что делает этот метод по крайней мере таким же эффективным, как итерация вручную.

Konrad Rudolph 31.12.2008 14:46

@Konrad, у меня нет никаких данных. Но мы регулярно профилируем наш код внутри компании, а я профилирую несколько внешних проектов. В достаточно сложном проекте это никогда не было узким местом. Это медленнее, но как только вы добавляете реальное приложение поверх, это не заметно.

JaredPar 31.12.2008 18:19

@Kronrad (прод.) Компилятор может встроить этот код, но в настоящее время компилятор C# не сильно переписывает ваш код в целях оптимизации. AFAIK, он никогда не вставляет лямбду. Хотя это, безусловно, возможно. Посмотрите на F#. Они сильно переписывают код.

JaredPar 31.12.2008 18:21

Как указал Джаред, есть различия.

Но, как всегда, не волнуйтесь, если вы не знаете, что это узкое место. И если это узкое место, вероятно, потому, что списки большие, и в этом случае вам следует подумать об использовании более быстрого поиска - хеш-таблицы или двоичного дерева, или даже просто сортировки списка и выполнения двоичного поиска даст вам log (n) производительность, которая будет иметь гораздо большее влияние, чем настройка вашего линейного корпуса.

Если поиск по вашему списку слишком медленный как есть, вы, вероятно, добьетесь большего успеха, чем линейный поиск. Если вы можете сохранить список отсортированным, вы можете использовать двоичный поиск, чтобы найти элемент за время O (lg n).

Если вы ищете лот весь, подумайте о замене этого списка Словарём, чтобы индексировать ваши объекты по имени.

Технически производительность во время выполнения версии делегата будет немного хуже, чем у другой версии, но в большинстве случаев вам будет трудно заметить какую-либо разницу.

Более важным (IHMO) является время выполнения кода, позволяющее написать то, что вы хотите, а не то, как вы этого хотите. Это имеет большое значение в ремонтопригодности.

Этот исходный код:

string needle = "example";
foreach (FooObj foo in _list)
{
    if (foo.Name == needle)        
        return foo;
}

требует, чтобы любой сопровождающий прочитал код и понял, что вы ищете конкретный элемент.

Этот код

string needle = "example";
return _list.Find(
    delegate(FooObj foo) 
    {
        return foo.Name == needle;
    });

дает понять, что вы ищете конкретный предмет, - быстрее для понимания.

Наконец, этот код, использующий возможности C# 3.0:

string needle = "example";
return _list.Find( foo => foo.Name == needle);

делает то же самое, но в одной строке, что еще быстрее для чтения и понимания (ну, во всяком случае, если вы поймете лямбда-выражения).

Таким образом, учитывая, что производительность альтернатив почти одинакова, выберите тот, который упрощает чтение и сопровождение кода.

«Я работаю с базой кода, где списки должно быть часто обыскивают для одного элемента»

Лучше изменить структуру данных на Dictionary вместо List, чтобы повысить производительность.

Аналогичный вопрос был задан для List.ForEach vs. foreach-iteration (foreach против someList.Foreach () {}).

В этом случае List.ForEach был немного быстрее.

Другие вопросы по теме