从C#List <t>中删除项目是否会保留其他项目的订单?</t>

时间:2011-08-05 19:26:58

标签: c# generics collections

最近,我写了很多看起来像这样的代码:

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()区块中添加了评论,特别提及。

6 个答案:

答案 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 + 1index的数组副本;这是批量转移和“挤压”阵列的项目。但肯定没有对元素进行重新排序。

答案 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是您要删除的内容(它包含实际数据)。