我正在尝试执行以下操作:
int count = 50;
List<float> myList = new List<float>(50);
output[0] = 0.0f;
但我收到错误:
ArgumentOutOfRangeException: Argument is out of range.
Parameter name: index
显然,我误解了List(或者任何其他集合?)的初始容量是什么。有人可以向我解释一下初始容量是什么吗?
答案 0 :(得分:5)
列表底部有一个数组。这样可以减少内存重新分配的数量。添加到50个元素。这需要时间和内存,并提供垃圾收集器的功能。
这就是为什么List(T).Capacity是一件事。
100个.Add
的
Method A: Dictionary, no capacity
Time: 1350 ms
Method B: Dictionary, has capacity
Time: 700 ms
Method C: Dictionary, const capacity
Time: 760 ms
Method D: Dictionary, over-large capacity
Time: 1005 ms
Method E: List, no capacity
Time: 1010 ms
Method F: List, accurate capacity
Time: 575 ms
因此,当您添加100个元素时,预分配似乎只需要一半的时间。虽然我不是过早优化的粉丝,但如果你知道你的列表有多大,那么给CLR一个暗示值得它。
答案 1 :(得分:2)
列表是动态的。它可以在运行时动态添加项目和删除项目
List实现使用基础数组来存储列表的项目。
基础数组的长度称为列表的容量
列表中的每个项目都存储在基础数组中
当尝试向列表中添加新项目并且基础数组中不再有空格时,将创建一个新的和更大的数组。
旧数组中的所有项目都将移动到新数组,新数组还包含更多新项目空间
然后将新数组设置为列表的基础数组(旧数组将被处理)。
分配新阵列然后将项目从旧阵列移动到新阵列的这种操作是昂贵的(性能明智)。
因此,如果您从一开始就知道要添加到列表中的项目数量。然后您可以创建具有所需初始容量的基础数组的列表。
因此,您的列表在运行时分配新的底层数组的可能性会更小
您可以通过调用List(T) Constructor(int32)来设置基础数组的初始长度
您可以通过调用List(T).Capacity Property
答案 2 :(得分:1)
列表仍然是空的,因为没有元素,但在内部它已经为50个项目保留了内存。这是一个优化,因为当您向列表中添加项目时,它必须分配两倍大小的新数组,然后将旧数组中的项目复制到新数组。
请注意,在此转换期间,例如从256个项目中添加一个项目,并且在复制到新阵列时,它总共分配了(256 + 512)768个内存值。基本上是以前容量的三倍。根据数组的大小,这可能会导致内存不足异常。因此,如果你事先知道你只会在列表中添加257项,那么你可以预先使用257的容量。这将避免这种三倍的内存使用,因为阵列不必增大,因为它已经足够大了。请注意,在未压缩的堆上分配非常大的数组这一事实会增加发生内存不足异常的可能性,因此碎片可能导致难以为整个阵列找到连续的内存块。因此,有时这个问题会导致您在看起来有足够的可用内存时遇到内存异常。当然,如果可以的话,你可能想要避免这么大的列表。
总之,在您事先知道将要添加多少项目或可以估计(可能填充更大)的情况下,随时使用容量。
答案 3 :(得分:1)
首先,你为什么得到例外:
通过定义初始capacity,您只需指定列表可以存储的元素数量,然后再调整大小。
这并不意味着您的列表中有可访问的索引。你的清单仍然是空的,所以当你这样做时:
myList[0] = 0.0f;
你得到一个例外,但如果你这样做:
List<float> myList = new List<float>(50);
myList.Add(0);
myList[0] = 2.0f;
现在你不会得到异常,因为索引0
有一个元素。
现在问题的第二部分,容量实际意味着什么:
请参阅:List<T>.Capacity
属性:
容量是List之前可以存储的元素数 需要调整大小,而Count是元素的数量 实际上在列表中。
容量始终大于或等于Count。如果计数超过 添加元素时的容量,容量增加了 在复制旧数据之前自动重新分配内部数组 元素和添加新元素。
答案 4 :(得分:0)
它用于预分配列表使用的内部数组。 (此内部数组的大小由List.Capacity
给出。)
然而,仅仅因为这个内部数组具有一定的大小,并不意味着列表中的那些元素可用;它们不会,直到您(例如)使用List.Add()
添加适当的元素。列表的当前可寻址大小由List.Count
给出。
当你向列表中添加一个新元素时,它会首先检查内部数组中是否有足够的空间,如果有,只需增加一个内部计数器并使用内部数组中的一个插槽。
如果没有足够的空间,它会分配两倍于旧缓冲区大小的新缓冲区,将旧缓冲区中的所有元素复制到新缓冲区中,然后丢弃旧缓冲区。然后它可以使用新缓冲区中的一个槽。这显然是一项非常昂贵的操作。
通过设置初始大小,可以避免一些(或所有)缓冲区重新分配。
一个常见的用途是当您知道列表的最大可能大小,但不知道最小大小时,并且您希望在执行尽可能少的重新分配时填充列表。