Общий список FindAll() против foreach

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

В общем, что было бы лучшей и быстрой реализацией?
1. Перебирать каждый элемент в списке, сохранять каждое совпадение в новый список и возвращать его.

foreach(string s in list)
{ 
    if(s == "match")
    {
       newList.Add(s);
    }
} 

return newList;

Или
2. Используя метод FindAll и передавая ему делегат.

newList = list.FindAll(delegate(string s){return s == "match";});

Разве они оба не работают за ~ O(N)? Что было бы лучшей практикой здесь?

С уважением, Джонатан


person jon37    schedule 23.03.2009    source источник


Ответы (8)


Вам обязательно следует использовать метод FindAll или эквивалентный метод LINQ. Кроме того, рассмотрите возможность использования более краткой лямбды вместо вашего делегата, если это возможно (требуется C# 3.0):

var list = new List<string>();
var newList = list.FindAll(s => s.Equals("match"));
person Egil Hansen    schedule 23.03.2009

В этом случае я бы использовал метод FindAll, так как он более лаконичен. , и IMO, имеет более удобочитаемость.

Вы правы в том, что они оба будут работать за время O(N), хотя foreach оператор должен быть немного быстрее, поскольку ему не нужно выполнять вызов делегата (делегаты несут небольшую накладные расходы, в отличие от прямого вызова методов).

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

Как всегда, проверьте, где находятся узкие места, и действуйте соответствующим образом.

person casperOne    schedule 23.03.2009

Джонатан,

Хороший ответ вы можете найти в главе 5 (соображения производительности) Linq To Action.

Они измеряют a для каждого поиска, который выполняется около 50 раз и дает foreach = 68 мс за цикл / List.FindAll = 62 мс за цикл. На самом деле, вероятно, в ваших интересах просто создать тест и убедиться в этом самим.

person matt_dev    schedule 23.03.2009

List.FindAll равно O(n) и будет выполнять поиск по всему списку.

Если вы хотите запустить свой собственный итератор с foreach, я бы рекомендовал использовать оператор yield и, если возможно, вернуть IEnumerable. Таким образом, если вам в конечном итоге понадобится только один элемент вашей коллекции, это будет быстрее (поскольку вы можете остановить вызывающую программу, не исчерпав всю коллекцию).

В противном случае придерживайтесь интерфейса BCL.

person Reed Copsey    schedule 23.03.2009

Любая разница в производительности будет крайне незначительной. Я бы предложил FindAll для ясности или, если возможно, Enumerable.Where. Я предпочитаю использовать методы Enumerable, потому что они обеспечивают большую гибкость при рефакторинге кода (вы не зависите от List<T>).

person Daniel Pratt    schedule 23.03.2009

Да, обе реализации - O (n). Им нужно просмотреть каждый элемент в списке, чтобы найти все совпадения. С точки зрения удобочитаемости я бы также предпочел FindAll. По соображениям производительности см. LINQ в действии (глава 5.3). Если вы используете C# 3.0, вы также можете применить лямбда-выражение. Но это только вишенка на торте:

var newList = aList.FindAll(s => s == "match");
person olli-MSFT    schedule 23.03.2009

Я с лямбдами

List<String> newList = list.FindAll(s => s.Equals("match"));
person MRFerocius    schedule 23.03.2009

Если команда C# не улучшила производительность для LINQ и FindAll, следующая статья предполагает, что for и foreach превзойдут LINQ и FindAll при перечислении объектов: LINQ о производительности объектов.

Эта статья была датирована мартом 2009 года, как раз перед тем, как изначально был задан этот пост.

person William Niu    schedule 24.06.2011