java ArrayList扩容机制jdk16

  • 1、无参构造
  • 2、有参构造



本文基于jdk16,其他版本jdk思想是相同的,只不过调用的方法可能有所不同,本文如果存在问题,请求大佬给予指点。

1、无参构造

ArrayList使用无参构构造,第一次添加将ArrayList中存放数据的elementData容量扩容为10

java arraylist内存溢出_java

从上面断点处进入ArrayList的无参构造中

//ArrayList的元素都被存储在elementData中   此处使用transient关键字代表该字段为瞬时态,无法序列化
transient Object[] elementData;
//无参构造初始化赋值给elementData的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

//ArrayList的无参构造
public ArrayList() {
    //将elementData赋值为空数组
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

第一次给arrayList添加元素

java arraylist内存溢出_ci_02

从上面断点中进入以下源码

//此处的modCount意义为arrayList被修改的次数   防止多线程操作ArrayList导致出现错误
protected transient int modCount = 0;
//此处的size代表的是当前需要赋值的索引值,int类型默认为0
private int size;

public boolean add(E e) {
    //被修改的次数加1
    modCount++;
    //调用add方法
    add(e, elementData, size);
    return true;
}

从上面add(e, elementData, size);进入到下面的方法中

private void add(E e, Object[] elementData, int s) {
    //此处判断当前需要赋值的索引值是否等于elementData的长度
    //如果相等的话,说明当前的elementData的容量不足了,需要进行扩容
    if (s == elementData.length)
        //调用grow()函数对elementData进行扩容
        elementData = grow();
    //将需要添加的元素添加到elementData中
    elementData[s] = e;
    //对size进行加1,为下一添加的元素的索引值
    size = s + 1;
}

上面代码中调用的grow()函数

private Object[] grow() {
    //上面代码中调用的grow()函数
    return grow(size + 1);
}

上面代码中进入的函数,第一次添加元素,走else

private static final int DEFAULT_CAPACITY = 10;

//上面代码中进入的函数
//minCapacity此处为1
private Object[] grow(int minCapacity) {
    //目前的容量即elementData的当前长度
    int oldCapacity = elementData.length;
    //如果当前的elementData不为无参构造所赋值的默认空数组进入if中,否则进入else
    if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //newCapacity为新的容量 大概意思为在oldCapacity的基础上在加上oldCapacity >> 1的值
        //即相当于int newCapacity = oldCapacity + (oldCapacity >> 1)
        //也相当于int newCapacity = oldCapacity + oldCapacity * 0.5  
        //newCapacity为oldCapacity乘以1.5
        int newCapacity = ArraysSupport.newLength(oldCapacity,
                minCapacity - oldCapacity, /* minimum growth */
                oldCapacity >> 1           /* preferred growth */);
        return elementData = Arrays.copyOf(elementData, newCapacity);
    } else {
        //从DEFAULT_CAPACITY和minCapacity中取出最大数,创建数组
        //因为只有elementData为无参构造所赋值的默认空数组才会进入这里
        //说明当使用无参构造创建ArrayList时,当第一次添加元素时,会将容量扩容为10
        return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
    }
}

从上面代码返回回去,到下面代码中,将元素添加到elementData中

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    //将需要添加的元素添加到elementData中
    elementData[s] = e;
    //对size进行加1,为下一添加的元素的索引值
    size = s + 1;
}

第一次添加元素,调用无参构造,将容量扩容为了10,若之后还需再次扩容,则会将容量扩展为原来的1.5倍

java arraylist内存溢出_赋值_03

当不需要扩容时,会在下面的语句中直接进行元素的添加,并返回

private void add(E e, Object[] elementData, int s) {
    //此处判断当前需要赋值的索引值是否等于elementData的长度
    //此时if语句不满足,直接进行赋值
    if (s == elementData.length)
        elementData = grow();
    //将需要添加的元素添加到elementData中
    elementData[s] = e;
    //对size进行加1,为下一添加的元素的索引值
    size = s + 1;
}

中间一些步骤与上面相同,不同的只有扩容时不同,此处只展示扩容时,中间步骤跳过

//此时minCapacity为size加1,之前添加了10个元素,所以这里为11
private Object[] grow(int minCapacity) {
    //oldCapacity为当前elementData的长度即为10
    int oldCapacity = elementData.length;
    //if判断满足,进入if中
    if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //newCapacity为新的容量 大概意思为在oldCapacity的基础上在加上oldCapacity >> 1的值
        //即相当于int newCapacity = oldCapacity + (oldCapacity >> 1)
        //也相当于int newCapacity = oldCapacity + oldCapacity * 0.5  
        //newCapacity为oldCapacity乘以1.5
        int newCapacity = ArraysSupport.newLength(oldCapacity,
                minCapacity - oldCapacity, /* minimum growth */
                oldCapacity >> 1           /* preferred growth */);
        //将得到的新的elementData返回  通过Arrays.copyOf得到的数组会保留之前的数据
        return elementData = Arrays.copyOf(elementData, newCapacity);
    } else {
        return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
    }
}

2、有参构造

如果使用指定容量的构造器,则初始elementData的容量为指定容量

java arraylist内存溢出_java arraylist内存溢出_04

从当前断点进入到有参构造中

private static final Object[] EMPTY_ELEMENTDATA = {};

//ArrayList有参构造   initialCapacity创建对象时传入的容量大小
public ArrayList(int initialCapacity) {
    //如果容量大小大于0,则创建指定容量的数组进行返回
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    //如果容量大小等0,则创建的数组为空数组,与无参构造相同
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

总结:

1、ArrayList中维护了一个Object类型的数组elementData

transient Object[] elementData;

2、当使用无参构造创建ArrayList对象时,则初始化的elementData容量为0,当添加第一个元素时,会扩容为10,如需再次扩容,则扩容为原来容量的1.5倍(代码中是在原来容量的基础上加上原来容量数右移1位)

3、当使用有参构造创建ArrayList对象时,则初始化的elementData容量为指定容量大小,如果需要再次扩容,则扩容为原来容量的1.5倍(代码中是在原来容量的基础上加上原来容量数右移1位)