ArrayList扩容机制

本文详细探讨了Java中ArrayList的扩容机制,从ArrayList的主要成员变量、构造方法到添加元素的过程。在添加元素时,首次添加会根据构造方法决定初始容量,后续添加若超出现有容量,会进行动态扩容。扩容主要通过grow()方法实现,将容量扩大为原来的1.5倍,确保能容纳更多元素。当使用默认构造方法创建ArrayList时,首次插入超过10个元素时才会扩容;而指定容量构造时,插入超过初始容量的1.5倍时扩容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ArrayList的主要成员变量:

private static final int DEFAULT_CAPACITY = 10;
//数组默认初始容量
 
private static final Object[] EMPTY_ELEMENTDATA = {};
//定义一个空的数组实例以供其他需要用到空数组的地方调用
 
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//定义一个空数组,跟前面的区别就是,
//这个空数组是用来判断ArrayList第一添加数据的时候要扩容多少。
//默认的构造器情况下返回这个空数组
 
transient Object[] elementData;
//数据存的地方。
//它的容量就是这个数组的长度。
//同时只要是使用默认构造器,第一次添加数据的时候容量扩容为DEFAULT_CAPACITY = 10
 
private int size;
//当前数组的长度

size:ArrayList的实际存放数据个数,即我们常说的list长度,size()函数

动态扩容,就是动态改变elementDate.length,从而达到扩容。
实际的size一般都小于elementDate.length
可以通过trimToSize()函数将elementDate的容量改为数据实际的个数,去除多余的容量。

ArrayList的构造方法有三种:

在这里插入图片描述

//带初始容量的初始化 
public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
 
 
//无参初始化
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
 
 
//collection作为参数
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

如果使用Array(),内部elementData是DEFAULTCAPACITY_EMPTY_ELEMENTDATA。
如果使用ArrayList(0),elementData是EMPTY_ELEMENTDATA。
如果使用ArrayList(3),elementData是 new 的 Object[3]。
如果使用ArrayList(otherCollection),elementData是这个对象的toArray()方法返回的数组。
这里要判断:

  1. 如果这个返回数组是空的,那直接使用自己的EMPTY_ELEMENTDATA。
  2. 如果不为空,但不是Object[]类型的数组,要转化。

Arrays.copyOf()返回的是新的数组。

添加元素:

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
}

private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

 private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
 
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
}

现在假设第一种情况:

List<int> list = new ArrayList<>();
list.add(10);

这是空ArrayList第一次添加数据,是一个整数10。
函数调用流程:
调用函数ensureCapacityInternal(1);

因为预计做完add操作后,list的元素个数会变为1.

调用ensureExplicitCapacity(calculateCapacity(elementData, 1));
(calculateCapacity(elementData, 1)的返回值为10;

判断elementData是不是DEFAULTCAPACITY_EMPTY_ELEMENTDATA
也就是判断list是不是使用ArayList()创建的,并且还没有被动过
如果是的话,判断10和1(add操作完后预计元素个数)哪个大,返回大的

即调用ensureExplicitCapacity(10);
在这个函数内判断10-0>0,

需要的容量是10,目前的数组长度是0,所以需要扩容至10

grow(10),list现在变为有10个空位。
第一个空位插入10,size变为1.

当插入第2个数时:
调用ensureCapacityInternal(2)
调用ensureExplicitCapacity(calculateCapacity(elementData, 2));
调用(calculateCapacity(elementData, 2)返回2
相当于调用ensureExplicitCapacity(2)
在函数中判断2-10>10,

需要的容量为2,现有的数组长度为10,长度足够

为false,不grow。

当插入第11个数时:
调用ensureCapacityInternal(11)
调用ensureExplicitCapacity(calculateCapacity(elementData,11));
调用(calculateCapacity(elementData, 11)返回11
相当于调用ensureExplicitCapacity(11)
在函数中判断11-10>10,

需要的容量为11,现有的数组长度为10,不够,需要扩容至11

为true,调用grow(11)。

总结:
如果使用ArrayList()创建的对象,在第一次插入时扩容至10,之后在10以内不扩容,
如果超过10,需要扩容。
如果使用ArrayList(3)创建的对象,在插入第4个元素时需要扩容。
使用ArrayList(anotherList)创建的对象,插入元素超过时需要扩容。

扩容机制

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

现在假设数组长度为10,调用grow(11)。
oldCapacity是10
newCapacity是1.5倍即15
判断这个更大的数组(15个空位)是否足以容下11个元素,本例可以
判断这个长为15的数组是否超过了规定的最大长度MAX_ARRAY_SIZE,显然没有
调用elementData = Arrays.copyOf(elementData, 15);
创建一个新的长为15的数组,把已有的10个元素复制进去,在第11个位置插入数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忍者算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值