我正在学习 C# 并且基本上知道数组和 s 之间的区别List
,最后一个是通用的并且可以动态增长,但我想知道:
- 元素是
List
按顺序位于类似数组的堆中,还是每个元素“随机”位于不同的位置? - 如果这是真的,这是否会影响访问速度和从内存中检索数据?
- 如果这是真的,这就是使数组比
List
s 快一点的原因吗?
我们先来看第二个和第三个问题:
如果这是真的,那会影响从内存中访问和数据检索的速度吗?
如果这是真的,是什么让 array 比 list 快一点?
.NET 中只有一种类型的“本机”集合(对于 .NET,我指的是 CLR,因此是运行时):数组(从技术上讲,如果您考虑string
一种类型的集合,那么有两种本机类型的集合:-) )(从技术上讲,第 2 部分:并非所有您认为是数组的数组都是“本机”数组......只有基于 0 的单维数组是“本机”数组。类型数组T[,]
不是,第一个数组元素没有索引 0 are not) 。所有其他集合(除了LinkedList<>
)都建立在它之上。如果你看一下List<T>
withIlSpy
你会发现在它的底部有T[]
一个为 the 添加int
的Count
(the T[].Length
is the Capacity
)。显然,数组比 a 快一点List<T>
因为要使用它,你就少了一个间接性(你直接访问数组,而不是访问访问列表的数组)。
我们来看第一个问题:
列表元素是否顺序位于堆中的数组或每个元素随机位于不同的位置?
在内部基于数组,显然List<>
它像数组一样记住它的元素,所以在一个连续的内存块中(但请注意,对于List<SomeObject>
whereSomeObject
是引用类型,列表是引用列表,而不是对象列表,所以引用被放在一个连续的内存块中(我们将忽略计算机的高级内存管理,“连续的内存块”这个词并不准确”,最好说“一个连续的地址块”) )
(是的,甚至Dictionary<>
和HashSet<>
构建在数组之上。相反,可以在不使用数组的情况下构建树状集合,因为它更类似于 a LinkedList
)
CIL
一些额外的细节:该语言(编译的 .NET 程序中使用的中间语言)中有四组指令与“本机”数组一起使用:
Newarr
Ldelem
和家人Ldelem_*
Stelem
和家人Stelem_*
ReadOnly
(别问我它的用途,我不知道,文档也不清楚)
如果你看一下,OpCodes.Newarr
你会在 XML 文档中看到这条评论:
// Summary:
// Pushes an object reference to a new zero-based, one-dimensional array whose
// elements are of a specific type onto the evaluation stack.
是的,List 中的元素是连续存储的,就像数组一样。List 实际上在内部使用数组,但这是一个您不需要真正关心的实现细节。
当然,为了从该陈述中获得正确的印象,您还必须了解一点 .NET 中的内存管理。即值类型和引用类型之间的区别,以及这些类型的对象是如何存储的。值类型将存储在连续的内存中。对于引用类型,引用将存储在连续的内存中,而不是实例本身。
使用 List 的优点是类内部的逻辑为您处理分配和管理项目。您可以在任何地方添加元素,从任何地方删除元素,并增加集合的整个大小,而无需做任何额外的工作。当然,这也是 List 比数组稍慢的原因。如果为了满足您的请求而必须进行任何重新分配,则会在分配一个新的、更大尺寸的数组并将元素复制到其中时对性能造成影响。但这不会比您编写代码以使用原始数组手动执行要慢。
如果您的长度要求是固定的(即,您永远不需要增加/扩展数组的总容量),您可以继续使用原始数组。它甚至可能比 List 稍微快一点,因为它避免了额外的开销和间接性(尽管这会被 JIT 编译器优化)。
如果您需要能够动态调整集合的大小,或者您需要 List 类提供的任何其他功能,只需使用 List。性能差异几乎无法察觉。