ArrayList:大小如何增加?

Posted

技术标签:

【中文标题】ArrayList:大小如何增加?【英文标题】:ArrayList: how does the size increase? 【发布时间】:2011-05-25 22:30:11 【问题描述】:

我有一个关于 Java ArrayList 的基本问题。

当使用默认构造函数声明和初始化ArrayList 时,会创建10 个元素的内存空间。现在,当我添加第 11 个元素时,会发生什么?是否会创建具有 20 个(或更多)元素容量的新内存空间(这需要将元素从第一个内存位置复制到新位置)或其他什么?

我检查了here。但我没有找到答案。

请分享知识。 谢谢。

【问题讨论】:

最好的方法是实际阅读源代码。但要小心。这里是龙。 Here 是 OpenJDK 6 中 ArrayList 的来源。请注意,它有很多实现(GNU Classpath、Apache Harmony、OpenJDK 等),它们可能会有所不同。 大多数实现增长了 1.5 倍:octoperf.com/blog/2018/03/19/java-arraylist 【参考方案1】:

ArrayList 是 Collection 接口的类。 Java 是开源语言,我们可以更改它的实现。 这里java预定义的实现是: 新容量 = (currentCapacity*3)/2 + 1; 要么 在 JAVASE8 中 新容量=旧容量+(旧容量>>1);

【讨论】:

这与this other answer中的相同。【参考方案2】:

让我们看看这个测试用例(jdk1.8)

@Test
    public void testArraySize() throws Exception 
        List<String> list = new ArrayList<>();
        list.add("ds");
        list.add("cx");
        list.add("cx");
        list.add("ww");
        list.add("ds");
        list.add("cx");
        list.add("cx");
        list.add("ww");
        list.add("ds");
        list.add("cx");
        list.add("last");
    

1)插入“last”时在该行设置断点

2)转到ArrayList的add方法 你会看到

ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;

    进入ensureCapacityInternal方法,该方法调用ensureExplicitCapacity

    private void ensureExplicitCapacity(int minCapacity) modCount++;

          // overflow-conscious code
          if (minCapacity - elementData.length > 0)
              grow(minCapacity);
      
              return true;
    

在我们的示例中 minCapacity 等于 11 11-10 &gt; 0 因此将调用 grow 方法

5)

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);
    

让我们描述每个步骤:

    oldCapacity = 10 默认情况下,java 8 arraylist 的容量是 10 ,你可以通过在构造函数中传递另一个值来覆盖它

    int newCapacity = oldCapacity + (oldCapacity &gt;&gt; 1); 这里 newCapacity 等于 oldCapacity 加上 oldCapacity 右移一位 (oldCapacity is 10 这是二进制表示 00001010 向右移动一位将使 00000101 是十进制的 5,因此 newCapacity10 + 5 = 15

    如果 (newCapacity - minCapacity new ArrayList<>(1)),当您添加第二个元素时,newCapacity 将等于 1(oldCapacity) + 0 (moved to right by one bit) = 1 在这种情况下,newCapacity 小于 minCapacity 并且 elementData( 存储数据的 arrayList 内的对象 arrayObject[]) 不能容纳新元素,因此 newCapacity 将等于 minCapacity

    如果(新容量 - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);

检查数组大小是否达到 MAX_ARRAY_SIZE(即 Integer.MAX - 8)Why the maximum array size of ArrayList is Integer.MAX_VALUE - 8?

    最后它将旧值复制到长度为 15 的 newArray

【讨论】:

【参考方案3】:

Java 中的 ArrayList 在满后会增长 50% 的原始容量。

【讨论】:

欢迎来到 Stack Overflow。与其他答案相比,此答案增加了哪些新信息?【参考方案4】:

    Arraylist 默认容量为 10。

    [1,2,3,4,5.....10]

    如果你想在java中增加Arraylist的大小,你可以应用这个

    公式-

    int newcapacity,当前容量;

    新容量=((当前容量*3/2)+1)

    arralist 将为 [1,2,3,4,5.....10,11],arraylist 容量为 16。

【讨论】:

嗨 Shobha,欢迎来到 ***。请不要将您的整个答案插入代码块中。请仅将代码块用于代码并将文本留在外面。此外,您可能希望在粘贴代码之前对其进行测试:您不能在变量名中使用空格,因此current capacity 不是变量,您的代码将无法编译。【参考方案5】:

直到 JDK 6,容量随着公式 newCapacity = (oldCapacity * 3/2) + 1 增长。

在 JDK 7 及更高版本中,公式更改为 newCapacity = oldCapacity + (oldCapacity &gt;&gt; 1)

所以如果初始容量为10,那么新容量将为16 in JDK615 in above JDK7

【讨论】:

【参考方案6】:

(oldSize * 3)/2 + 1

如果您使用默认构造函数,则 ArrayList 的初始大小将为 10 否则您可以在创建 ArrayList 的对象时传递数组的初始大小。

示例:万一默认构造函数

List<String> list = new ArrayList<>();
list.size()

例子:万一参数化构造函数

List<String> list = new ArrayList<>(5);
list.size()

【讨论】:

【参考方案7】:

从JDK源代码中,我找到了下面的代码

int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);

【讨论】:

【参考方案8】:

ArrayList 在以下情况下确实会增加负载因子的大小:

初始容量: 10 负载系数: 1(即当列表已满时) 增长率: current_size + current_size/2

上下文:JDK 7

在向ArrayList 添加元素时,以下public ensureCapacityInternal 调用和其他私有方法调用在内部发生以增加大小。这就是动态增加ArrayList 大小的原因。 在查看代码时,您可以通过命名约定来理解逻辑,因此我没有添加明确的描述

public boolean add(E paramE) 
        ensureCapacityInternal(this.size + 1);
        this.elementData[(this.size++)] = paramE;
        return true;
    

private void ensureCapacityInternal(int paramInt) 
        if (this.elementData == EMPTY_ELEMENTDATA)
            paramInt = Math.max(10, paramInt);
        ensureExplicitCapacity(paramInt);
    
private void ensureExplicitCapacity(int paramInt) 
        this.modCount += 1;
        if (paramInt - this.elementData.length <= 0)
            return;
        grow(paramInt);
    

private void grow(int paramInt) 
    int i = this.elementData.length;
    int j = i + (i >> 1);
    if (j - paramInt < 0)
        j = paramInt;
    if (j - 2147483639 > 0)
        j = hugeCapacity(paramInt);
    this.elementData = Arrays.copyOf(this.elementData, j);

【讨论】:

【参考方案9】:

在 Jdk 1.6 中: 新容量=(当前容量*3/2)+1;

在 Jdk 1.7 中:

int j = i + (i >> 1); 这和 新容量=(当前容量*1/2)+当前容量;

ex:size 会增加 :10-->15-->22-->33

【讨论】:

【参考方案10】:
static int getCapacity(ArrayList<?> list) throws Exception 
            Field dataField = ArrayList.class.getDeclaredField("elementData");
            dataField.setAccessible(true);
            return ((Object[]) dataField.get(list)).length;
    

在修改arraylist时使用上述方法检查大小。

【讨论】:

【参考方案11】:

ArrayList 的默认容量是 10。 一旦Capacity达到最大容量,ArrayList的Size将为16,一旦达到最大容量16,ArrayList的大小将为25,并根据Data size不断增加.....

怎么样?这是答案和公式

New capacity = (Current Capacity * 3/2) + 1

所以,如果默认容量是10,那么

Current Capacity = 10
New capacity = (10 * 3/2) + 1
Output is 16

【讨论】:

【参考方案12】:

ArrayList 的大小总是随着n+n/2+1 而增加。

【讨论】:

你能补充一些解释吗?很短【参考方案13】:

当使用默认构造函数声明和初始化 ArrayList 时,会创建 10 个元素的内存空间。

没有。初始化 ArrayList 时,会为空数组分配内存。仅在将第一个元素添加到 ArrayList 时才会为默认容量 (10) 分配内存。

 /**
 * The array buffer into which the elements of the ArrayList are stored.
 * The capacity of the ArrayList is the length of this array buffer. Any
 * empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
 * DEFAULT_CAPACITY when the first element is added.
 */
private transient Object[] elementData;

附:没有足够的声誉来评论问题,所以我将其作为答案,因为之前没有人指出这个错误的假设。

【讨论】:

【参考方案14】:

上下文 java 8

我在Oracle java 8实现的上下文中给出了我的答案,因为在阅读了所有答案后,我发现gmgmiller给出了java 6上下文中的答案,并且在上下文中给出了另一个答案java 7. 但是java 8是如何实现大小增加的还没有给出。

在java 8中,大小增加行为与java 6相同,见ArrayList的grow方法:

   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);
    

关键代码是这一行:

int newCapacity = oldCapacity + (oldCapacity >> 1);

这么清楚,增长因子也是1.5,和java 6一样。

【讨论】:

对于无法获得“1.5”推导的人。将 oldCapacity 视为 x,则 newCapacity = x + x/(2^1) = x +x/2 = 1.5x 操作符 '>>',它在移位时进行符号扩展,对于某些人来说可能有点混乱,因为它不被视为 div 操作符,因为它不与负数一起使用价值观。在这种情况下,它只是由于长度始终> = 0而起作用。作为 div 操作符 '>>>' 应该主要使用。【参考方案15】:

当我们尝试将一个对象添加到数组列表中时,

Java 检查 以确保现有数组中有足够的容量来容纳新对象。如果没有,创建一个更大的新数组旧数组被复制到新数组,使用 Arrays.copyOf 和新数组分配给现有数组。

查看下面的代码(取自 GrepCode.com 上的 Java ArrayList 代码)。

编辑:

public ArrayList() 构造一个初始容量为 10 的空列表。

public ArrayList(int initialCapacity)我们可以指定初始容量。

public ArrayList(Collection c) 构造一个包含指定集合元素的列表,按集合迭代器返回的顺序排列。

现在,当我们使用 ArrayList() 构造函数时,我们会得到一个 Size=10 的 ArrayList 在列表中添加第 11 个元素时,在 ensureCapacity() 方法中创建新的 Arraylist。

使用以下公式:

  int newCapacity= (oldCapacity * 3)/2 +1;

【讨论】:

那个动作 :) 当你在近 2 年后在谷歌搜索中得到答案时【参考方案16】:

Sun 的 JDK6:

我相信它会增长到 15 个元素。没有把它编码出来,而是看着 grow() jdk中的代码。

int newCapacity 然后 = 10 + (10 >> 1) = 15。

/**
 * Increases the capacity to ensure that it can hold at least the
 * number of elements specified by the minimum capacity argument.
 *
 * @param minCapacity the desired minimum capacity
 */
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);

来自 Javadoc,它说这是来自 Java 2 及更高版本,因此它在 Sun JDK 中是安全的选择。

编辑:对于那些不知道乘数 1.5int newCapacity = oldCapacity + (oldCapacity &gt;&gt; 1); 之间有什么联系的人

&gt;&gt; 是右移运算符,可将数字减半。 因此,int newCapacity = oldCapacity + (oldCapacity &gt;&gt; 1); => int newCapacity = oldCapacity + 0.5*oldCapacity; => int newCapacity = 1.5*oldCapacity ;

【讨论】:

是的,阅读源代码是理解行为的最佳方式。我也阅读了代码并得到了相同的答案。对于 jdk 8 也是如此。【参考方案17】:

当使用默认值声明和初始化 ArrayList 构造函数,将创建 10 个元素的内存空间。 现在,当我添加第 11 个元素时,会发生什么

ArrayList 创建一个具有以下大小的新对象

即旧容量*3/2+1 = 10*3/2+1 =16

【讨论】:

【参考方案18】:

arraylist 的默认大小是 10。当我们添加第 11 个时 ....arraylist 增加大小(n*2)。将旧arraylist中存储的值复制到大小为20的新arraylist中。

【讨论】:

这不会添加 2010 年原始答案中未提及的任何内容。 当你添加第 11 个元素时,容量将是 15 而不是 20。【参考方案19】:

创建一个新数组,并复制旧数组的内容。这就是您在 API 级别所知道的全部内容。引用the docs(我的重点):

每个ArrayList 实例都有一个容量。容量是用于存储列表中元素的数组的大小。它总是至少与列表大小一样大。随着元素被添加到 ArrayList,它的容量会自动增长。 除了添加一个元素具有恒定的摊销时间成本这一事实之外,没有具体说明增长策略的细节。

ArrayList 的特定实现(例如Sun 的)实际发生的情况而言,在他们的情况下,您可以在源代码中看到血淋淋的细节。但当然,依赖特定实现的细节通常不是一个好主意...

【讨论】:

将创建一个新数组并复制旧数组的内容。那么旧数组会在内存中还是被删除呢? @VikasKumarSingh:旧的有资格进行垃圾收集,就像任何不再有任何引用它的对象一样。何时(或即使)发生这种情况取决于 JVM。 复制元素的旧 ArrayList 会发生什么情况? GC 会回收该内存吗? @Ravi.Kumar:没有旧的ArrayList,只有旧的数组。是的,ArrayList 释放了它对旧数组的引用,这是对它的唯一引用,这意味着它有资格进行 GC。 @Denson - 每当 JVM 无法分配内存时,它要么直接死掉(或者尝试分配的速度太慢以至于它可能已经死了),要么成功抛出 OutOfMemoryError .在我看来(根据我 前的经验)它通常确实会抛出错误(为此目的预留了一些内存),但是 YMMV。【参考方案20】:

这将取决于实现,但来自 Sun Java 6 源代码:

int newCapacity = (oldCapacity * 3)/2 + 1;

ensureCapacity 方法中。其他 JDK 实现可能会有所不同。

【讨论】:

【参考方案21】:

通常,ArrayList 类型容器的内存会增加一倍。因此,如果您最初有 10 个项目的空间并且您添加了 10 个,则第 11 个项目将添加到一个新的(内部)20 个项目的数组中。这样做的原因是,如果数组以固定大小增量递增,则添加项目的增量成本从 O(n^2) 减少到每次内部数组已满时将大小加倍时的 O(n)。

【讨论】:

【参考方案22】:

会发生的情况是创建一个包含 n*2 个空格的新数组,然后复制旧数组中的所有项目,并将新项目插入第一个空闲空间。总而言之,这导致 ArrayList 的添加时间为 O(N)。

如果您使用的是 Eclipse,请安装 Jad 和 Jadclipse 以反编译库中保存的 JAR。我这样做是为了阅读原始源代码。

【讨论】:

以上是关于ArrayList:大小如何增加?的主要内容,如果未能解决你的问题,请参考以下文章

简易版Java ArrayList(增删改查)

ArrayList

ArrayList:大小如何增加?

ArrayList 如何增加大小

在Java中怎么修改ArrayList()中元素的值?

Arraylis类