是列表<;T>;真的是C#中的卧底阵列吗?
我一直在研究使用ILSpy的.NET库,在是列表<;T>;真的是C#中的卧底阵列吗?,c#,.net,C#,.net,我一直在研究使用ILSpy的.NET库,在System.Collections.Generic命名空间中遇到了List类定义。我看到该类使用了如下方法: // System.Collections.Generic.List<T> /// <summary>Removes all elements from the <see cref="T:System.Collections.Generic.List`1" />.</summary> public
System.Collections.Generic
命名空间中遇到了List
类定义。我看到该类使用了如下方法:
// System.Collections.Generic.List<T>
/// <summary>Removes all elements from the <see cref="T:System.Collections.Generic.List`1" />.</summary>
public void Clear()
{
if (this._size > 0)
{
Array.Clear(this._items, 0, this._size);
this._size = 0;
}
this._version++;
}
//System.Collections.Generic.List
///从中删除所有元素。
公共空间清除()
{
如果(此尺寸>0)
{
Array.Clear(此.\u项,0,此.\u大小);
这个。_size=0;
}
这个;
}
因此,List
类的Clear()
方法实际上使用了Array.Clear
方法。我见过许多其他的List
方法,它们在body中使用数组
这是否意味着List
实际上是一个undercover数组,或者List只使用数组方法的一部分
我知道列表是类型安全的,不需要装箱/拆箱,但这让我有点困惑。列表类本身不是数组。换句话说,它不是从数组派生的。相反,它封装了实现用来保存列表成员元素的数组 由于
List
提供对其元素的随机访问,并且这些元素被索引为0..Count-1
,因此使用数组存储元素是显而易见的实现。是,List
在内部使用数组存储项目,虽然在大多数情况下,数组实际上大于集合中的元素数,但它的末尾有一些额外的“填充”,这样您就可以添加新项,而无需每次重新分配内存。它通过一个单独的字段跟踪集合的实际大小(您可以在生成的代码中看到this.\u size
)。当您添加的元素超过当前数组的空间时,它将自动分配一个新的更大的数组——我认为是两倍大——并复制所有现有元素
如果您担心
列表
使用的内存过多,可以使用接受容量
参数的显式设置数组的大小(如果您事先知道大小),或者调用该方法以确保数组(接近)集合的实际大小。随机访问内存是一个数组,因此,从这个意义上说,从链表到堆等所有依赖随机访问内存来实现性能行为的数据结构都是建立在系统内存阵列上的。这更多的是一个关于中间有多少抽象层次的问题
当然,在现代虚拟内存机中,随机存取系统内存本身是一个抽象C++,它是建立在多层流水线高速缓存、非缓存RAM和磁盘的复杂虚拟内存模型上的。 < P>这让那些知道STD::列表的C++程序员感到惊讶。一个链接列表,包含在.NET以及LinkedList类中。和具有相同的性能特征,O(1)用于插入和删除 不过,一般来说,你应该避免这样做。链表在现代处理器上性能不佳。这在很大程度上取决于cpu缓存,以获得比执行内核慢很多倍的内存的合理性能。到目前为止,一个简单的数组是最能利用缓存的数据结构。访问元素时,后续元素也出现在缓存中的可能性非常高。链表的情况并非如此,元素往往分散在整个地址空间中,使得缓存很可能丢失。它们可能非常昂贵,高达200个周期,cpu什么也不做,只是等待内存子系统提供数据
但是一定要记住perf特性,添加或删除一个不在列表末尾的元素会花费O(n),就像数组一样。当阵列需要扩展时,一个大的列表可能会产生大量垃圾,预先设置Capacity属性有助于避免这种情况。更多关于这方面的信息,请参阅。另外,std::vector也有同样的问题。您希望它是一个链表还是什么?计算机没有魔力。列表只是一个数组,它可以根据需要自动增长,而不需要显式的重新分配。这是一种神奇的方法。如果你已经打开了ILSpy,并且你已经在查看列表的定义,为什么你不看看“this.\u items”的定义来回答你自己的问题呢?我很喜欢这个,不是因为我认为这是一个好问题(你自己已经回答了一半),而是因为SO上一些最好的.NET人员已经回答了。问这个问题有什么问题?我从未使用过ILSpy,我也不知道这个列表是如何秘密的。因此,这个问题帮助我提高了自己的知识水平,我相信它对其他人也有帮助。值得补充的是,动态增长数组的标准方法,即每次增长时将其大小加倍,在数组大小上保持线性,从而继续提供O(1)平均插入时间。@PieterGeerkens不是平均插入时间。这意味着在大多数情况下,可以在固定时间内插入任何索引。相反,过度分配的动态数组提供了在ω(1)时间内摊销的最坏情况追加。也就是说,即使是在数组上最糟糕的操作序列中,每次
append
调用的摊销时间也是恒定的:虽然任何一次调用都可能需要线性时间,但此后您可以频繁地追加O(1)时间,以便最终达到平衡。@delnan:您当然是正确的;我的术语错了。非常感谢。今天是我学习新东西的好日子。我可能喜欢这篇文章只是为了你对术语的解释。好吧,我知道列表不是数组,因为我们有两个不同的类,但如果它在引擎盖下使用数组,这意味着它是对已经存在的数组概念的某种升级,或者我错了?它不是“升级的数组”。它是一个列表,实现IList,并支持列表样式