【问题标题】:Why do List<T>.Add and List<T>.Remove copy the elements into a new array instead of actually adding and removing?为什么 List<T>.Add 和 List<T>.Remove 将元素复制到一个新数组中,而不是实际添加和删除?
【发布时间】:2016-09-10 15:12:08
【问题描述】:

我在ReferenceSource 中查看List&lt;T&gt; 并发现List&lt;T&gt;.Add(还有RemoveRemoveAt)实际上将内部列表数组(T[] _items)复制到一个带有新修改的新数组中;他们实际上调用了Array.Copy 方法,即调用CLR-internal 方法Copy。我不知道该内部方法 (Copy) 是如何工作的,但如果它按照所谓的方式工作(复制),那么我相信这将是一种在列表;我想知道为什么没有实际的添加和删除?

【问题讨论】:

  • Add 只需要在调整大小时复制数组,这应该很少发生。如果您从列表末尾以外的任何位置删除,则必须将已删除项之后的元素向下移动到源数组中,这就是 Array.Copy 调用正在做的事情。
  • 如果您更仔细地查看该代码,我想您会发现它不会在每次添加或删除时扩展/复制数组。
  • 但是在某些时候,整个元素都会被复制到一个新的数组中;这就是我要说的;为什么没有实际的添加和删除。
  • 数组有一个固定的大小,所以增加大小是必要的。它在必要时将数组大小加倍,这是一种摊销 O(1) 复杂度的算法。没有比这更好的了。这会浪费地址空间,但不会浪费物理内存,这要归功于程序运行的按需分页虚拟内存操作系统。否则,数组对于提高引用的局部性非常重要,这对现代处理器来说非常重要,并确保最有效地使用处理器缓存。如果您对最终集合大小有一个很好的猜测,那么设置容量很有用。

标签: c# .net arrays list data-structures


【解决方案1】:

Add 方法并不总是复制内部数组。当数组填满项目时,它的项目将移动到一个新的,大小是原来的两倍。

Remove 而言。可以看到,只有index小于_size,它才会执行复制操作。这意味着如果您尝试从列表中删除最后一个元素,则无需调整内部数组的大小,它只是删除最后一个元素。但是,如果你想从中间移除一个元素,你需要在移除的索引之后创建“移位”左元素。

这就是为什么如果您要从List&lt;T&gt; 中的任意索引中删除许多元素,建议您使用另一种数据结构,例如LinkedList

【讨论】:

  • 但是,如果你想从中间删除一个元素,你需要创建一个全新的数组,其中不包括你刚刚删除的元素。这不是真的,它只是是在删除的索引之后“移动”左边的元素。
  • 谢谢,我猜没仔细研究。
  • +1 .. 很好的说明,但在某些时候,整个元素将被复制到一个新数组中;这就是我要说的;为什么没有实际的添加和删除。
  • @AlyEl-Haddad:所有元素都被复制的原因是,当分配的数组已满时,我们需要更多空间用于元素。无法为额外的数组分配添加更多空间,因此您必须创建一个新的更大的数组。这就像您尝试将一个项目添加到一个已经装满的盒子中一样。你不能只是让盒子更大。您必须获得一个更大的盒子并将原始盒子的全部内容移动到新的更大的盒子中。简而言之,一旦分配了一个数组,它的大小就不能改变。
猜你喜欢
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
  • 2019-07-02
  • 2020-05-23
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多