RemoveRange()方法如何在List <>中工作?

Tar*_*rec 7 .net c# ienumerable list

如标题.我知道它可能在删除项目之前和之后合并了2个子列表,但是在删除LAST元素时该方法的行为如何?换句话说:它是否以某种方式复制了删除索引之前所有元素?我只是对在巨型列表上使用RemoveRange的性能感到好奇(假设说5000个元素)只是为了删除fe中的最后2个.

如果它复制了那么,是否有办法更改一些设置List大小的内部变量(并将其余的已分配元素视为垃圾)?

我只是设法找到一个信息,它是一个O(n)复杂度算法,但我不确定该情况下的"n"是列表大小,还是要删除的项目数.

任何提示都会很高兴.

Ser*_*rvy 15

它将做的是在项目范围结束后取出每个项目,并将其移除并在列表中按移除的项目数量向上移动.就性能影响而言,在移动的项目范围结束后,每个项目将有一个副本.这意味着当从末尾移除时它将表现最佳(它将是O(1))并且在从开始移除时执行最差(O(n)).

例如,请考虑以下列表:

index - value

0 - A
1 - B
2 - C
3 - D
4 - E
5 - F
6 - G

如果我们调用RemoveRange(2, 2) 那么我们将从索引2开始删除两个项目,因此我们将删除C和D.

这意味着需要将E复制到索引2,然后需要将F复制到索引3,并且需要将G复制到索引4.在删除最后一个项目后,每个项目都有一个复制操作.

请注意,由于您可以将整个内存块"向上"移动两个,因此最终在实践中更有效地单独复制每个项目.对于计算机内存来说,将整个内存块向上移动一定数量的字节比将大量内存小部分移动到不同的任意位置要容易得多.它虽然具有相同的渐近复杂性.


Eri*_*ert 14

List<T>只是一个数组的包装器,_items在下面的代码中调用.数组中的插槽可能比列表中的项目多,因此_size用于跟踪实际有效的数量.当你做RemoveRange(index, count)...

_size -= count;
if (index < _size)
    Array.Copy(_items, index + count, _items, index, _size - index);
Array.Clear(_items, _size, count);
Run Code Online (Sandbox Code Playgroud)

...它将项目从过去的范围复制到现在空白的空间中,并清除旧项目.

如果要删除接近大列表开头的范围,那么成本与列表的大小成比例,因为必须向下移动很多东西.

如果要移除靠近大型列表末尾的大范围,则复制很便宜但清除费用很高,因此成本将与移除范围的大小成比例.

  • @Tarec:大用户是正确的; 假设它是一个长长的对象列表,可以保留大量内存,并且可以删除大量内存.你希望它们能在下一个系列中被收回.垃圾收集器只看到一个数组; 它不知道没有人会再次访问它们.清除内存使列表告诉GC列表不再使用这些引用. (3认同)
  • 您能否更深入地解释一下为什么在第二种情况下清算费用昂贵?据我了解,我们必须仅清除数组中受影响的元素,在大列表的情况下,其数量不会像“_size”或“_items.Length”那么大。例如,如果您要删除大列表中靠近开头或结尾的小范围。 (2认同)
  • @Jodrell可能不是.使用链表时,内存局部性的丢失会导致性能非常差,即使对于简单的任务(例如迭代链表)也是如此.虽然其中许多操作的Big O值很低,但在大多数实际情况下,链表在净胜利中并不常见. (2认同)
  • @Tarec如果列表未被清除并且它持有引用类型或具有字段到引用类型的结构,则它们不能被垃圾收集. (2认同)