Безопасно ли удалять из SortedList во время итерации - PullRequest
3 голосов
/ 23 апреля 2010

Мой вопрос: безопасно ли перечислителю удалить элемент из SortedList?

SortedList<decimal, string> myDictionary;
// omitted code

IEnumerator<decimal, string> enum = myDictionary.GetEnumerator();

while(enum.MoveNext)
{
  // is it ok to remove here?
  myDictionary.Remove(enum.Current.Key);
}

Ответы [ 6 ]

8 голосов
/ 23 апреля 2010

Это вызовет исключение - вы не можете изменять коллекцию, пока выполняете ее итерацию.

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

4 голосов
/ 23 апреля 2010

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

var removeList = new List<decimal>();
foreach (var item in myDictionary)
{
    // have a condition which indicates which items are to be removed
    if (item.Key > 1)
    {
        removeList.Add(item.Key);
    }
}

. Или, если вы просто пытаетесь получить элементы для удаления, используйте LINQ

var removeList = myDictionary.Where(pair => pair.Key > 1).Select(k => k.Key).ToList();

Затем просто удалите их из списка.

// remove from the main collection
foreach (var key in removeList)
{
    myDictionary.Remove(key);
}
2 голосов
/ 23 апреля 2010

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

Этот метод имеет немного лучший профиль памяти, поскольку он не требует дополнительных затрат, в отличие от увеличения O (n) с использованием отдельного списка для отслеживания удалений. Он также будет иметь профиль производительности O (n ^ 2), а не O (n ^ 2 * log (n)). Метод RemoveAt имеет значение O (n), поскольку он должен выполнять копирование массива. Метод Remove добавляет операцию O (log (n)) для поиска индекса перед внутренним вызовом RemoveAt. Все это, вероятно, не имеет значения для вас, но полезно знать, если вы столкнетесь с ситуациями, в которых много символов «n».

var myDictionary = new SortedList<decimal, string>();

// omitted code

int i = 0;
while (myDictionary.Count > 0 && i < myDictionary.Count)
{
  if (/* predicate to use for removal */)
  {
    myDictionary.RemoveAt(i);
  }
  else
  {
    i++;
  }
}
2 голосов
/ 23 апреля 2010

Нет.InvalidOperationExcpetion выбрасывается.Я согласен, что уже перечисленные элементы могут быть удалены, поскольку существует фиксированный индекс.Однако проблема заключается в следующем:

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

2 голосов
/ 23 апреля 2010

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

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

0 голосов
/ 17 апреля 2013

Другое решение:

            int counter= MyDictionary.Count;
            if (counter == 0)
                return;

            for (int i = 0;  i < counter;i++)
            {
                KeyValuePair<MyIdentifier, MyValue> key = (KeyValuePair<MyIdentifier, MyValue>)MyDictionary.ToArray()[i];
                MyIdentifier identifier = null;

                if (key.Key != null)
                    identifier = key.Key as MyIdentifier;

                if (identifier != null)
                    if (MyCondition)
                    {
                        MyDictionary.Remove(identifier);
                        counter--;
                    }
            }
...