我一直在用Reflector查看.NET的列表和 ArrayList 实现。
查看添加(T项)时,我遇到了这个。 EnsureCapacity (this._size + 1):
public void Add(T item)
{
if (this._size == this._items.Length)
{
this.EnsureCapacity(this._size + 1);
}
this._items[this._size++] = item;
this._version++;
}
所以EnsureCapacity看起来像这样:
private void EnsureCapacity(int min)
{
if (this._items.Length < min)
{
int num = (this._items.Length == 0) ? 4 : (this._items.Length * 2);
if (num < min)
{
num = min;
}
this.Capacity = num;
}
}
为什么内部容量默认为4然后以2的倍数递增(即:double)?
答案 0 :(得分:7)
关于在需要调整大小时加倍,原因如下。假设您要将n
项插入List
。我们将调整列表大小不超过log n
次。因此,将n
项插入List
将花费最坏情况O(n)
时间,因此插入在amortized time中保持不变。此外,浪费的空间量高于n
。任何恒定比例增长的策略都将导致不变的摊销插入时间和线性浪费空间。比恒定比例增长更快的速度可以导致更快的插入,但代价是浪费更多的空间。比恒定比例增长慢的生长可以减少浪费的空间,但代价是插入速度较慢。
默认容量是浪费很少的内存(并且从小时开始不会有任何伤害,因为我们刚才看到,从时间/空间的角度看,双倍调整大小的策略是好的。)
答案 1 :(得分:6)
4是一个很好的默认值,因为大多数集合中只有几个项目。完成递增是为了确保每次添加项目时都不进行内存分配。
请参阅Joel关于内存使用情况的好文章,以及为什么分配双倍所需内容是一个好主意。
http://www.joelonsoftware.com/printerFriendly/articles/fog0000000319.html
以下是相关引用:
假设您编写了一个智能strcat函数,可以自动重新分配目标缓冲区。它应该始终将其重新分配到所需的确切大小吗?我的老师和导师Stan Eisenstat建议,当你调用realloc时,你应该总是将以前分配的内存大小加倍。这意味着你永远不必调用realloc超过lg n次,即使对于大字符串也具有相当的性能特征,并且你永远不会浪费超过50%的内存。
另外,列表&lt;&gt;和字典&lt;&gt;现在默认为10,但我敢打赌他们有相同的递增逻辑。
答案 2 :(得分:0)
我敢打赌,这样你就可以创建没有多次分配的小型列表。尺寸加倍是为了简单而不是使用复杂的缩放算法。
答案 3 :(得分:0)
似乎4是一个合理的权衡,大到足以容纳有4个项目或更少的频繁情况,并且没有太多浪费的物品。
每次分配增加的容量增加一倍,确保它可以容纳容器中已有项目数量的两倍。这是与C ++的向量容器类似的算法。