2012-01-09 3 views
9

В настоящем время, лучший, что я мог думать о:Эффективного удаления элемента внутри «Еогеаспа»

bool oneMoreTime = true; 
while (oneMoreTime) 
{ 
    ItemType toDelete=null; 
    oneMoreTime=false; 
    foreach (ItemType item in collection) 
    { 
     if (ShouldBeDeleted(item)) 
     { 
      toDelete=item; 
      break; 
     } 
    } 
    if (toDelete!=null) 
    { 
     collection.Remove(toDelete); 
     oneMoreTime=true; 
    } 
} 

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

+0

возможно дубликат [Как условно удалить элементы из коллекции .NET] (http://stackoverflow.com/questions/653596/how-to-conditionally-remove-items-from-a-net- коллекция) –

ответ

31

Метод «RemoveAll» является лучшим.

Другой распространенный метод:

var itemsToBeDeleted = collection.Where(i=>ShouldBeDeleted(i)).ToList(); 
foreach(var itemToBeDeleted in itemsToBeDeleted) 
    collection.Remove(itemToBeDeleted); 

Другой распространенный метод заключается в использовании «для» цикла, но убедитесь, что вы идете назад:

for (int i = collection.Count - 1; i >= 0; --i) 
    if (ShouldBeDeleted(collection[i])) 
     collection.RemoveAt(i); 

Другой распространенный метод заключается в добавлении предметы, которые не удаляются в новую коллекцию:

var newCollection = new List<whatever>(); 
foreach(var item in collection.Where(i=>!ShouldBeDeleted(i)) 
    newCollection.Add(item); 

И теперь у вас есть две коллекции. Техника, которая мне особенно нравится, если вы хотите, чтобы в итоге были созданы две коллекции, - использовать неизменные структуры данных. При неизменной структуре данных «удаление» элемента не изменяет структуру данных; он возвращает вам новую структуру данных (которая, если возможно, повторно использует биты из старой), у которой нет элемента, который вы удалили. При неизменных структурах данных вы не изменяя вещь вы итерацию, так что нет никаких проблем:

var newCollection = oldCollection; 
foreach(var item in oldCollection.Where(i=>ShouldBeDeleted(i)) 
    newCollection = newCollection.Remove(item); 

или

var newCollection = ImmutableCollection<whatever>.Empty; 
foreach(var item in oldCollection.Where(i=>!ShouldBeDeleted(i)) 
    newCollection = newCollection.Add(item); 

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

+0

Вы когда-нибудь использовали расширение 'Reverse', используя foreach - я просто наткнулся на него здесь http://stackoverflow.com/a/10541025/706363? Почему это не будет более широко используемым вариантом? Есть ли какие-то серьезные последствия для производительности или что-то там происходит? – ppumkin

+0

@ppumkin: Попробуйте написать реализацию 'Reverse' для' IEnumerable', которая не реализует 'IList',' ICollection' и т. Д. Какова эффективность памяти и времени вашей реализации? –

+0

Мое выполнение не является критическим по времени или ресурсу. Я использую расширение 'IEnumerable.Reverse ' в Linq в списке, и, похоже, он отлично работает в 'foreach' - Thats, почему я спрашиваю, почему этот пример не используется чаще, а не все это для (от 1 до 0) наоборот, как и в вашем ответе. Использует ли Reverse Extension действительный вариант? Можете ли вы добавить его в свой ответ или что-то не так с использованием Reverse Extension от Linq в сочетании с foreach? Просто подумал, что ваше мнение принесет наибольший вес из-за вашего опыта. – ppumkin

13

Как только я закончил печатать, я вспомнил, что для этого есть лямбда-способ.

collection.RemoveAll(i=>ShouldBeDeleted(i)); 

Лучший способ?

+2

Просто FYI: это может быть преобразовано в группу методов. collection.RemoveAll (ShouldBeDeleted). – ahawker

1

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

for (int i = collection.Count-1; i >= 0; i--) 
{ 
    if(ShouldBeDeleted(collection[i]) 
     collection.RemoveAt(i); 
} 

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

1

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

Вы можете использовать цикл for, чтобы удалить отдельные элементы и соответствующим образом настроить индекс. Однако это может быть подвержено ошибкам. В зависимости от реализации базовой коллекции также может быть дорого удалять отдельные элементы. Например, удаление первого элемента из List<T> скопирует все элементы, оставшиеся в списке, в списке.

Лучшее решение часто, чтобы создать новую коллекцию, основанную на старом:

var newCollection = collection.Where(item => !ShouldBeDeleted(item)).ToList(); 

Использование ToList() или ToArray(), чтобы создать новую коллекцию или инициализировать ваш конкретный тип коллекции из IEnumerable возвращенного пункта Where().

1

Переднее вариация на обратной for цикла:

for (int i = 0; i < collection.Count;) 
    if (ShouldBeDeleted(collection[i])) 
     collection.RemoveAt(i) 
    else 
     i++; 
0

Просто используйте два списка,

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

var filteredItems = new List<ItemType>(); 

foreach(var item in collection){ 
    if(!ShouldBeDeleted(item)) 
     filteredItems.Add(item); 
} 
Смежные вопросы