列表的默认容量是多少?

71

List 的默认容量是多少?


9
根据默认容量来操作,我认为是一个漏洞。只需使用具有初始容量参数的构造函数,您就可以确定容量。请直接使用该构造函数。 - OregonGhost
4
如果你了解List<T>的工作原理,那么这绝对不是一个bug,并且这个列表很有可能为空,或者包含少于4个元素。 - Harry
根据Thorarin的回答:https://dotnetfiddle.net/OoRIHl - Daniel B
1
https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,38 - David Klempfner
8个回答

69

实际上,它从容量为0开始。当您添加第一个元素时,当前实现会分配4个容量。之后,如果需要扩展,则容量会不断加倍,以保证平摊O(1)操作。

请记住,这是当前的行为。您不应该依赖它成为情况。这应该演示当前的行为:

List<int> list = new List<int>();
int capacity = list.Capacity;
Console.WriteLine("Capacity: " + capacity);

for (int i = 0; i < 100000; i++)
{
    list.Add(i);
    if (list.Capacity > capacity)
    {
        capacity = list.Capacity;
        Console.WriteLine("Capacity: " + capacity);
    }
}

你能详细解释一下“保证摊销O(1)操作”的意思吗? - David Klempfner
基本上意味着无论你需要多少容量...越多越好。 - Matthew Czarnek
1
@DavidKlempfner 如果您每次将容量加倍,那么您将为大约2n个项目(包括过去的分配)分配空间-因此,由于这是针对n.Add的,每个.Add大约需要2个任意时间单位,也就是平摊的O(1)时间。(当然,这假设分配需要线性时间) - somebody

64

你为什么不试一下呢?

Console.WriteLine("Default capacity of a List: " + new List<int>().Capacity);

这个答案适用于所有具有List的.NET版本。在我的版本上,它恰好是0。


5
“为什么不试试呢?”这个问题给人一种负面的印象,我认为这是一个错误。即使是“容易尝试”的事情,也应该提出问题以便进行文档记录。 - julealgon

52

根据MSDN无参数构造函数文档中的示例,使用以下代码创建的列表的初始容量为:

List<string> x = new List<string>();

我发现初始容量为0的情况并没有文档保证,而且也没有说明resize策略(即可能当前是每次double,最小值为4,但在.NET 5.0中,它可能会变成triple,最小值为128)。基本上你不应该依赖这种行为。


实际上它声明为“为空并具有默认的初始容量”。使用反射器,可以发现默认值为4。 - Brian Rasmussen
11
@Brian:不,这种情况下默认的初始容量为0。4是一个必须有元素的列表的第一个容量。重要的是它没有被记录下来是0还是4。在下一个版本中,容量可以变成100而不会破坏任何记录下来的行为。 翻译:在这种情况下,默认的初始容量为0。对于一个必须含有元素的列表来说,它的第一个容量是4。重要的是,文档中没有明确说明它的默认值是0还是4。在下一个版本中,容量可能会变成100,而不会影响任何已经记录下来的行为。 - Jon Skeet
1
@Brian:无论文档怎么说,根据Capacity属性,它都是从0开始的。 - Thorarin
2
@Jon:我完全同意这是一个实现细节,这是重点。我理解你的观点。4是“默认容量”,一旦有东西被添加到列表中就会发生变化。 - Brian Rasmussen

7

List的默认容量为4个项目(在插入初始项目之后,否则为0大小)

var list = new List<int>();
list.Add(1);

Assert.AreEqual(4, list.Capacity);

是的,它像指数函数一样呈指数增长,4、8、16、32、64...但默认值肯定是0。 - David Hedlund
8
请注意,它会使初始容量翻倍,但不一定是2的次幂。如果您给出初始容量为3,则会得到3、6、12、24等容量。 - yoyo
1
因此,默认容量为0,而不是4。按照您的逻辑,“默认”容量为100万(在插入几千个项目以开始良好的情况下)。 - greenoldman

3

默认容量为0,但如果您按照下面的方式创建一个空列表 [List1],并且该列表中有以下元素 [List2],则添加的元素数量变为N除以2。默认容量会有所不同。

List<int> List1 = new List<int>(); //count=0, capacity=0
List<int> List2 = new List<int>(){ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; //count=11, capacity=16

当添加数组类型的元素时,开发人员使用ReSize方法扩展了数组。它的处理速度非常慢。在开发列表类型时,容量得到了一定程度的增加。这个比率被开发为大于2的N。
如果向列表中添加的成员数量超过当前容量,则会将其容量翻倍。 从列表中删除一些值不会降低容量。容量仅会增加,而不是减少。即使使用Clear方法也不会影响容量。

默认值是4或8吗?不是0吗?在0处,您肯定应该调整大小为0*2,这是行不通的。 - WDUK

1

如果您大致知道要在List(或Stack、Queue)中存储多少项,则应使用容量。在这种情况下,您将避免内存复制。内存复制是因为底层的Lists(Stacks和Queues)依赖于数组来存储它们的项目。该数组大小是您的容量,但它与列表大小不同。由于列表的大小需要大于数组的大小,因此List实现将分配一个更大的数组(可能少2倍),并将所有项目从旧数组复制到新数组以及新增的项目。

因此,如果您知道您的列表中可能有50到60个项目,请创建一个容量为60的列表,就不会发生内存释放。

注意:看起来垃圾收集器将不必清理旧数组。


0
如果您使用这种方法,可以确保每个版本中的任何必要信息都得到正确更新。
List<string> dinosaurs = new List<string>();
Console.WriteLine("\nInit:\nCapacity: {0}", dinosaurs.Capacity);
Console.WriteLine("Count: {0}", dinosaurs.Count);

dinosaurs.Add("Deinonychus");
Console.WriteLine();
foreach (string dinosaur in dinosaurs)
{
    Console.WriteLine(dinosaur);
}
Console.WriteLine("Capacity: {0}", dinosaurs.Capacity);
Console.WriteLine("Count: {0}", dinosaurs.Count);

dinosaurs.Add("Mamenchisaurus");
dinosaurs.Add("Amargasaurus");
dinosaurs.Add("Compsognathus");
dinosaurs.Add("Son of Compsognathus");
Console.WriteLine();
foreach (string dinosaur in dinosaurs)
{
    Console.WriteLine(dinosaur);
}
Console.WriteLine("Capacity: {0}", dinosaurs.Capacity);
Console.WriteLine("Count: {0}", dinosaurs.Count);

/* In C# 11 This code example produces the following output:

Init:
Capacity: 0
Count: 0

Deinonychus
Capacity: 4
Count: 1

Deinonychus
Mamenchisaurus
Amargasaurus
Compsognathus
Son of Compsognathus
Capacity: 8
Count: 5

==>> default: 0
*/


0
这段代码的意思是将所有元素压缩到同一行,以确保容器有足够的空间存储另一个元素。
int num = this._items.Length == 0 ? 4 : this._items.Length * 2;

从mscorlib 4.0.0.0反汇编得到的 - 当然,正如Jon所说,这不能保证未来不会改变(到目前为止仍然保持在0、4、8、16...)。

当然,您可以自己设置,使其为3、9、27等。


1
你的意思是3、6、12等等吗?(它总是加倍,从提供的初始容量开始。) - yoyo
1
@yoyo,我认为当tomkuj说“当然,你可以自己设置它,这样它可以是3、9、27等。”时,他只是演示了在List初始化中可以选择作为容量值的几个任意随机数,而不是演示翻倍。 - jboeke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接