Удаляет ли элементы из списка С# List <T> заказы других предметов?
В последнее время я написал много кода, который выглядит так:
List<MyObject> myList = new List<MyObject>();
...
for(int i = 0; i < myList.Count; ++i)
{
if(/*myList[i] meets removal criteria*/)
{
myList.RemoveAt(i);
--i; //Check this index again for the next item
//Do other stuff as well
}
}
и я просто стал немного параноидальным, что, возможно, List не сохраняет порядок объектов при удалении. Я не знаю спецификации С# достаточно хорошо, чтобы точно знать. Может ли кто-нибудь подтвердить, что я либо или не прошу о неприятностях с этим шаблоном?
EDIT: Возможно, мне следует пояснить, что приведенное выше является очень упрощенным примером, и многое другое происходит, если элемент нужно удалить, поэтому я не думаю, что List<T>.RemoveAll()
здесь ужасно применимо. Хотя это хорошая функция. Я добавил комментарий в блок if()
выше, чтобы конкретно упомянуть об этом.
Ответы
Ответ 1
List<T>
всегда будет поддерживать относительный порядок при добавлении, вставке и удалении; это не было бы списком, если бы это не так.
Здесь код (ILSpy'ed) для RemoveAt()
:
public void RemoveAt(int index)
{
if (index >= this._size)
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}
this._size--;
if (index < this._size)
{
Array.Copy(this._items, index + 1, this._items, index, this._size - index);
}
this._items[this._size] = default(T);
this._version++;
}
Обратите внимание на копию массива с index + 1
на index
; что предметы переставляются оптом и "сжимают" массив вместе. Но, безусловно, переупорядочения элементов не существует.
Ответ 2
Вы действительно правы, List<T>.RemoveAt
не изменит порядок элементов списка.
Однако ваш фрагмент можно упростить, чтобы использовать List<T>.RemoveAll
следующим образом:
List<MyObject> myList = new List<MyObject>();
...
myList.RemoveAll(/* Removal predicate */);
Изменить следующий комментарий:
myList.Where(/* Removal predicate */).ToList().ForEach(/* Removal code */);
myList.RemoveAll(/* Removal predicate */);
Ответ 3
Хотя принятый ответ - отличный ответ на исходный вопрос, ответ Цикады предлагает альтернативный подход.
С CLR 4 (VS 2010) мы получаем еще один подход, который имеет еще одно преимущество только для выполнения предиката один раз для каждого элемента (и делает его удобным избежать написания предиката дважды в нашем коде).
Предположим, что у вас есть IEnumerable<string>
:
IEnumerable<string> myList = new[] {"apples", "bananas", "pears", "tomatoes"};
Вам нужно разделить его на два списка в зависимости от того, соответствуют ли элементы некоторым критериям:
var divided = myList.ToLookup(i => i.Length > 6);
Возвращаемый объект несколько похож на Dictionary
списков. Предположим, вы хотите сохранить те, которые соответствуют критериям:
myList = divided[true];
И вы можете использовать знакомый императивный цикл для работы с другими элементами:
foreach (var item in divided[false])
Console.WriteLine("Removed " + item);
Обратите внимание, что нет необходимости использовать List<T>
. Мы никогда не изменяем существующий список - мы просто делаем новые.
Ответ 4
Порядок должен поддерживаться. Лучший подход - перебросить список в обратном направлении:
for(int i = myList.Count - 1; i >= 0; i--)
{
if(/*myList[i] meets removal criteria*/)
{
myList.RemoveAt(i);
}
}
Или вы можете использовать метод RemoveAll
:
myList.RemoveAll(item => [item meets removal criteria]);
Ответ 5
От рефлектора:
public void RemoveAt(int index)
{
if (index >= this._size)
{
ThrowHelper.ThrowArgumentOutOfRangeException();
}
this._size--;
if (index < this._size)
{
Array.Copy(this._items, index + 1, this._items, index, this._size - index);
}
this._items[this._size] = default(T);
this._version++;
}
Так что, по крайней мере, с порядком выполнения MS 'items - items' не изменяется на RemoveAt
.
Ответ 6
Когда вы вызываете RemoveAt
, все элементы, следующие за удаляемым индексом, будут скопированы и сдвинуты вперед.
Сортировка списка позиций в порядке убывания и удаление элементов в этом порядке.
foreach (var position in positions.OrderByDescending(x=>x))
list.RemoveAt(position);
positions
- список индексов. list
- это тот, который вы хотите удалить (он содержит фактические данные).