ArrayList容量增长中的Java 6和Java 7之间的差异

时间:2018-05-31 09:33:31

标签: java math arraylist

我对如何使用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的使用没有任何影响,但为什么他们改变了这个功能呢?有任何表现原因吗?他们是否发现了旧的缺陷或错误?

1 个答案:

答案 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.ArrayListjava.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   调整大小时会产生负数。