java ArrayList扩容机制jdk16
- 1、无参构造
- 2、有参构造
本文基于jdk16,其他版本jdk思想是相同的,只不过调用的方法可能有所不同,本文如果存在问题,请求大佬给予指点。
1、无参构造
ArrayList使用无参构构造,第一次添加将ArrayList中存放数据的elementData容量扩容为10
从上面断点处进入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添加元素
从上面断点中进入以下源码
//此处的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倍
当不需要扩容时,会在下面的语句中直接进行元素的添加,并返回
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的容量为指定容量
从当前断点进入到有参构造中
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位)