ArrayList扩容机制底层源码分析
ArrayList概述
ArrayList属于List接口的实现类,是顺序容器。在底层是使用了Object类型的数组进行存储。因此如果存入的元素个数超过了数组的大小,就需要对数组进行扩容,因此我们就需要了解ArrayList的扩容机制。
源码分析
ArrayList扩容机制使用到的主要属性
private static final int DEFAULT_CAPACITY = 10; //第一次扩容时默认的扩容大小为10
//当实例化ArrayList传入的值为0时使用
private static final Object[] EMPTY_ELEMENTDATA = {};
//当我们使用ArrayList的无参构造器时使用
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//ArrayList的核心底层数组,存储我们添加的数据
//transient表示该数组不会被序列化
transient Object[] elementData;
//size : 存储当前数组中的元素个数
private int size;
构造方法
//无参构造
public ArrayList() {
//当我们使用ArrayList的无参构造时,会将elementData数组初始化为空数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//带参构造
public ArrayList(int initialCapacity) { //initialCapacity: 指定数组初始化大小
//如果传入的参数大于0
if (initialCapacity > 0) {
//将elementData数组初始化为initialCapacity大小
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) { //如果传入的参数等于0
//将elementData数组初始化为空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
//如果传入的参数为负数
//则会抛出IllegalArgumentException异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
小结:
- 当我们使用ArrayList的无参构造方法创建时,则默认将elementData数组赋值为空数组
- 当我们使用ArrayList的带参构造方法创建时,则将elementData数组赋值为指定的参数大小。
扩容方法:一次添加单个元素
add(E e)方法
//add方法 e代表要添加到集合中的元素
//该方法会返回一个boolean类型的值
public boolean add(E e) {
//ensureCapacityInternal() 确保数组的容量足够我们添加一个新的元素
ensureCapacityInternal(size + 1); //size为当前集合中元素的个数
elementData[size++] = e; //将elementData的size位置索引赋值为e,将元素添加到集合中
return true;
}
ensureCapacityInternal(int minCapacity)方法
private void ensureCapacityInternal(int minCapacity) { //minCapacity 如果要将元素添加到集合中至少需要的数组容量
//这里会首先调用calculateCapacity方法 判断数组是否是默认容量的数组
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
//然后调用ensureExplicitCapacity()方法
}
calculateCapacity(Object[] elementData, int minCapacity)方法
//elementData为当前集合的数组
//minCapacity为至少需要的数组容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果elementData为空数组,代表该数组还未进行第一次扩容
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//如果是第一次扩容,则扩容为DEFAULT_CAPACITY:10 ,如果插入这个元素需要的容量超过了10,则将至少需要的容量minCapacity进行返回
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
ensureExplicitCapacity(int minCapacity)方法
private void ensureExplicitCapacity(int minCapacity) { //minCapacity 为 calateCapacity()方法计算出需要的容量
modCount++; //数组修改的次数
//如果数组当前容量小于 minCapacity,则需要进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity); //grow() 扩容核心方法
}
grow(int minCapactiy)方法 扩容核心方法
private void grow(int minCapacity) {
//将 当前的数组大小 赋值给 oldCapacity(旧数组容量)
int oldCapacity = elementData.length;
//计算出newCapacity(新数组容量)
//oldCapacity >> 1 :将旧数组容量右移一位,表示除以2.
//oldCapacity + (oldCapacity >> 1) :计算结果为oldCapacity的1.5倍,然后赋值给newCapacity
int newCapacity = oldCapacity + (oldCapacity >> 1);
//判断预计新数组容量是否 满足 minCapacity的需求
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; //如果不满足,则将minCapacity直接赋值给newCapactiy作为新数组容量
//如果新数组容量超过了 数组的长度最大限制
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);//这里调用hugeCapactiy方法返回MAX_ARRAY_SIZE
//这一步将旧数组元素拷贝到新数组,并进行扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
最后会回到add(E e)方法,将新元素插入进去
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e; //将新的元素插入进去
return true; //返回true代表元素插入成功
}