我一直在使用ILSpy查看.NET库,并List<T>在System.Collections.Generic命名空间中遇到了类定义.我看到该类使用类似这样的方法:
// 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++;
}
Run Code Online (Sandbox Code Playgroud)
所以,类的Clear()方法List<T>实际上使用Array.Clear方法.我已经看到许多其他List<T>方法在体内使用Array的东西.
这是否意味着List<T>实际上是一个秘密数组或List只使用了一些Array方法?
我知道列表是类型安全的,不需要装箱/拆箱,但这让我有点困惑.
Dav*_*nan 44
列表类本身不是数组.换句话说,它不是从数组派生的.相反,它封装了一个数组,该实现用于保存列表的成员元素.
由于List<T>提供对其元素的随机访问,并且这些元素被索引0..Count-1,因此使用数组来存储元素是显而易见的实现.
Han*_*ant 31
这往往会让知道std :: list的C++程序员感到惊讶.一个链表,包含在.NET中以及LinkedList类.并且具有相同的性能特征,O(1)用于插入和删除.
但是你通常应该避免它.链接列表在现代处理器上表现不佳.这在很大程度上依赖于cpu缓存来获得合理的性能,其内存比执行核心慢很多倍.到目前为止,一个简单的数组是最有利于缓存的数据结构.访问元素给出了很高的可能性,即后续元素也存在于缓存中.对于链表而言情况并非如此,元素往往分散在整个地址空间中,可能导致缓存未命中.它们可能非常昂贵,多达200个周期,而cpu只是在等待内存子系统提供数据.
但是请记住perf特性,添加或删除不在List成本O(n)末尾的元素,就像数组一样.由于需要扩展阵列,因此大型List会产生大量垃圾,预先设置Capacity属性可以帮助避免这种情况.在这个答案中更多关于这一点.而对于std :: vector <>则完全相同.
Jer*_*odd 21
是的,List<T>在内部使用数组来存储项目,尽管在大多数情况下,数组实际上大于集合中的元素数量 - 它在末尾有一些额外的"填充",这样你就可以添加新的项目而不需要它每次重新分配内存.它使用单独的字段跟踪集合的实际大小(您可以this._size在生成的代码中看到).当您添加的元素多于当前数组所具有的空间时,它会自动分配一个新的更大的数组 - 我认为是两倍大 - 并复制所有现有元素.
如果您担心List<T>使用的内存超过必要的数量,可以使用接受参数的构造函数覆盖显式设置数组的大小capacity,如果您事先知道大小,或者调用TrimExcess()方法以确保数组是(接近)到集合的实际大小.