我对如何使用Java管理 ArrayList的容量增长(不是大小,但容量)有疑问。 当我们使用默认构造函数初始化ArrayList而不设置容量时,默认情况下容量设置为=。
此时,当我们向列表添加另一个元素时,Oracle文档说" As元素被添加到ArrayList,它的容量会自动增长。除了添加元素具有不变的摊销时间成本这一事实之外,未指定增长政策的细节。"
如果我们看一下Java内部,容量增长政策已经改变了它的功能。在Java 6之前它是:
(1) int newCapacity = (oldCapacity * 3)/2 + 1;
从Java 7(和> 7)开始:
(2) int newCapacity = oldCapacity + (oldCapacity >> 1);
但这两个数学系列略有不同。从默认值(10)开始,我们有:
(1)10,16,25,38,58,88,133,200,301,452 ...
(2)10,15,22,33,49,73,109,163,244,366 ...
我认为这对ArrayList的使用没有任何影响,但为什么他们改变了这个功能呢?有任何表现原因吗?他们是否发现了旧的缺陷或错误?
答案 0 :(得分:6)
OpenJDK source control history显示Martin Buchholz from Google已更改changeset 2350以修复错误JDK-6933217: Huge arrays handled poorly in core libraries。
新代码小心避免不必要的整数溢出。即使oldCapacity * 3
没有,oldCapacity * 3 / 2
也会溢出。新行oldCapacity + (oldCapacity >> 1)
不会。如果它确实溢出并且变为负数,则会有额外的代码将容量设置为Integer.MAX_VALUE
(或接近它)。
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
bug report的详细信息:
我注意到
java.util.ArrayList
,java.util.Hashtable
和{}中的错误java.io.ByteArrayOutputStream
当...的能力出现时 数据结构达到特定阈值。更多信息如下。当
ArrayList
的容量达到(2/3)*Integer.MAX_VALUE
时 大小达到其容量,添加或插入操作 调用时,容量只增加一个元素。请注意 在以下ArrayList.ensureCapacity
新摘录中摘录 容量设置为(3/2) * oldCapacity + 1
,除非此值不会 足以容纳所需的容量,在这种情况下设置 达到要求的能力。如果当前容量至少是(2/3)*Integer.MAX_VALUE
,然后(oldCapacity * 3)/2 + 1
溢出并且 解析为负数,导致设置新容量 达到要求的能力。这样做的主要后果是每个 后续的添加/插入操作会导致完全调整大小ArrayList
导致性能显着下降。int newCapacity = (oldCapacity * 3)/2 + 1; if (newCapacity < minCapacity) newCapacity = minCapacity;
...
有趣的是要注意关于摊销时间的任何陈述 添加/插入操作的复杂性,例如
ArrayList
中的操作 javadoc,与性能相关的错误无效。一 解决上述情况是设置新的容量 在初始大小计算时将数组支持到Integer.MAX_VALUE
调整大小时会产生负数。