Немного не по теме, но скажу, что я хочу удалить все 2 с из списка. Вот очень элегантный способ сделать это.
void RemoveAll<T>(T item,List<T> list)
{
while(list.Contains(item)) list.Remove(item);
}
с предикатом:
void RemoveAll<T>(Func<T,bool> predicate,List<T> list)
{
while(list.Any(predicate)) list.Remove(list.First(predicate));
}
+ 1 только для того, чтобы побудить вас оставить здесь свой ответ в учебных целях. Вы также правы в том, что это не по теме, но я не буду вам об этом рассказывать, потому что есть большая ценность, чтобы вы оставляли здесь свои примеры, опять же, исключительно для целей обучения. Я публикую этот ответ как правку, потому что публиковать его в виде серии комментариев будет неуправляемым.
Хотя ваши примеры короткие и компактные, ни один не является элегантным с точки зрения эффективности; первое плохо при O (n 2 ), второе абсолютно бездарно при O (n 3 ). Алгоритмическая эффективность O (n 2 ) является плохой, и ее следует по возможности избегать, особенно в коде общего назначения; эффективность O (n 3 ) ужасна и ее следует избегать во всех случаях, кроме случаев, когда вы знаете, что n всегда будет очень маленьким. Некоторые могут отказаться от своей «преждевременной оптимизации - корня всех злых» боевых топоров, но они делают это наивно, потому что они не совсем понимают последствия квадратичного роста, поскольку никогда не кодировали алгоритмы, которые должны обрабатывать большие наборы данных. В результате их алгоритмы обработки небольших наборов данных работают, как правило, медленнее, чем могли бы, и они даже не подозревают, что могли бы работать быстрее. Разница между эффективным алгоритмом и неэффективным алгоритмом часто неуловима, но разница в производительности может быть существенной. Ключом к пониманию производительности вашего алгоритма является понимание характеристик производительности примитивов, которые вы выбираете для использования.
В вашем первом примере list.Contains()
и Remove()
- оба O (n), поэтому цикл while()
с одним в предикате и другим в теле - O (n 2 ); ну, технически O (m * n), но оно приближается к O (n 2 ), так как количество удаляемых элементов (m) приближается к длине списка (n).
Ваш второй пример еще хуже: O (n 3 ), потому что каждый раз, когда вы звоните Remove()
, вы также звоните First(predicate)
, что также O (n). Подумайте об этом: Any(predicate)
проходит по списку , ища любой элемент, для которого predicate()
возвращает true. Как только он находит первый такой элемент, он возвращает истину. В теле цикла while()
вы затем вызываете list.First(predicate)
, который повторяет по списку второй раз , ища тот же элемент, который уже был найден list.Any(predicate)
. Как только First()
найдет его, он возвращает тот элемент, который передается в list.Remove()
, который проходит по списку в третий раз , чтобы еще раз найти тот же элемент, который был ранее найден Any()
и First()
, чтобы окончательно удалить его. После удаления весь процесс начинается сначала с немного более короткого списка , повторяя все циклы снова и снова и снова, начиная с начала каждый раз , пока, наконец, не останется больше элементов соответствующие предикаты остаются. Таким образом, производительность вашего второго примера равна O (m * m * n) или O (n 3 ), когда m приближается к n.
Лучшим вариантом для удаления всех элементов из списка, которые соответствуют некоторому предикату, является использование собственного метода List<T>.RemoveAll(predicate)
общего списка, который равен O (n), если ваш предикат равен O (1). Техника цикла for()
, которая проходит по списку только один раз, вызывая list.RemoveAt()
для каждого удаляемого элемента, может казаться равной O (n), поскольку кажется, что она проходит через цикл только один раз. Такое решение является более эффективным, чем ваш первый пример, но только на постоянный коэффициент, который с точки зрения алгоритмической эффективности незначителен. Даже реализация цикла for()
- это O (m * n), поскольку каждый вызов Remove()
- это O (n). Поскольку сам цикл for()
равен O (n), и он вызывает Remove()
m раз, рост цикла for()
равен O (n 2 ), когда m приближается к n.