Эффективное удаление элемента изнутри 'foreach' - PullRequest
14 голосов
/ 09 января 2012

На данный момент лучшее, что я могу придумать, это:

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

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

Ответы [ 5 ]

35 голосов
/ 09 января 2012

Лучше всего использовать метод «RemoveAll».

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

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

Другой распространенный метод - использовать цикл for, но убедитесь, что вы идете * 1006.* backwards :

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

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

14 голосов
/ 09 января 2012

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

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

Лучше?

2 голосов
/ 09 января 2012

прямое изменение обратной петли for:

for (int i = 0; i < collection.Count; )
    if (ShouldBeDeleted(collection[i]))
        collection.RemoveAt(i)
    else
        i++;
1 голос
/ 09 января 2012

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

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

Лучшим решением часто является создание новой коллекции на основе старой:

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

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

1 голос
/ 09 января 2012

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...