【问题标题】:Differences between Java 6 and Java 7 in ArrayList capacity growthJava 6 和 Java 7 在 ArrayList 容量增长上的区别
【发布时间】:2018-11-10 06:39:37
【问题描述】:

我有一个问题,关于 ArrayList 的容量增长(不是大小,而是容量)在 Java 中是如何管理的。 当我们使用默认构造函数初始化一个ArrayList而不设置容量时,默认设置容量=10。

此时,当我们向列表中添加另一个元素时,Oracle 文档说“随着元素被添加到 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 的使用没有任何影响,但是他们为什么要更改此功能?有任何性能原因吗?他们是否在旧版本中发现了缺陷或错误?

【问题讨论】:

    标签: java math arraylist


    【解决方案1】:

    OpenJDK 的source control history 显示它已被changeset 2350 中的Martin Buchholz from Google 更改以修复错误JDK-6933217: Huge arrays handled poorly in core libraries

    新代码小心避免不必要的整数溢出。即使oldCapacity * 3 / 2 不会溢出,oldCapacity * 3 也会溢出。新行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.Hashtablejava.io.ByteArrayOutputStream 当容量不足时出现 数据结构达到特定阈值。更多内容如下。

    ArrayList的容量达到(2/3)*Integer.MAX_VALUE时 size 达到其容量并且添加或插入操作是 调用时,容量仅增加一个元素。请注意 以下摘自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 在调整大小期间产生负数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-05
      • 2011-01-24
      • 2010-12-30
      • 1970-01-01
      • 2016-07-21
      • 1970-01-01
      相关资源
      最近更新 更多