最近,我写了很多看起来像这样的代码:
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在移除时不保留对象顺序。我不知道C#规范是否足够清楚。有人可以证实我或者不是在问这个模式有问题吗?
编辑:也许我应该澄清一下,上面是一个非常简单的例子,如果需要删除项目会发生更多事情,所以我认为List<T>.RemoveAll()
在这里非常适用。虽然这是一个很好的功能。我在上面的if()
区块中添加了评论,特别提及。
答案 0 :(得分:15)
List<T>
将始终维持相对顺序;如果没有,它就不会是一个清单。
这是 请注意从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
的数组副本;这是批量转移和“挤压”阵列的项目。但肯定没有对元素进行重新排序。
答案 1 :(得分:10)
你确实是对的,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 */);
答案 2 :(得分:5)
应保持订单。更好的方法是反向遍历列表:
for(int i = myList.Count - 1; i >= 0; i--)
{
if(/*myList[i] meets removal criteria*/)
{
myList.RemoveAt(i);
}
}
或者您可以使用RemoveAll
method:
myList.RemoveAll(item => [item meets removal criteria]);
答案 3 :(得分:5)
虽然接受的答案是对原始问题的一个很好的答案,但蝉的答案提出了另一种方法。
使用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 :(得分:3)
来自Reflector:
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'实施 - 项目的顺序在RemoveAt
上没有变化。
答案 5 :(得分:2)
当您致电RemoveAt
时,您删除的索引后面的所有元素都将被复制并向前移动。
按降序对排名列表进行排序,然后按顺序删除元素。
foreach (var position in positions.OrderByDescending(x=>x))
list.RemoveAt(position);
positions
是索引列表。 list
是您要删除的内容(它包含实际数据)。