作者在学习过程中,遇到了ArrayList扩容的底层原理这一问题,查看JDK源码后有所理解故写此博客记录下来。
ArrayList简介
ArrayList 是 java 集合框架中比较常用的数据结构。继承自 AbstractList,实现了 List 接口。基于数组实现容量大小动态变化。同时还实现了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 是支持快速访问、复制、序列化的。
注:有关实现RandomAccess便支持快速访问,请查看另一篇文章实现RandomAccess接口支持快速访问原理
ArrayList初始化
创建ArrayList对象时,若未指定集合容量,集合默认容量为0;
//默认创建一个空集合
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];
//ArrayList空参构造
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
当集合对象调用add方法存储数据时,进行容量初始化。底层实现如下:
//ArrayList集合对象添加元素方法
public boolean add(E var1) {
//扩容方法
this.ensureCapacityInternal(this.size + 1);
//存储元素
this.elementData[this.size++] = var1;
return true;
}
//添加元素时调用扩容方法
private void ensureCapacityInternal(int var1) {
//初始化状态时 默认容量变为10
if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
var1 = Math.max(10, var1);
}
//非初始化状态时,判断集合是否需要扩容
this.ensureExplicitCapacity(var1);
}
结论1:
当集合对象第一次添加元素的时候,集合大小扩容为10(若使用addAll方法添加元素,则初始化大小为10和添加集合长度的较大值)
当集合初始化完毕后,再次添加元素时。调用其ensureExplicitCapacity方法
private void ensureExplicitCapacity(int var1) {
++this.modCount;
//如果最小需要空间比elementData的内存空间要大,扩容
if (var1 - this.elementData.length > 0) {
//核心扩容方法
this.grow(var1);
}
}
private void grow(int var1) {
//获取原集合长度
int var2 = this.elementData.length;
//集合长度扩容1.5倍
int var3 = var2 + (var2 >> 1);
//如果扩容后仍小于添加集合的长度,新建集合长度为添加元素集合大小
if (var3 - var1 < 0) {
var3 = var1;
}
//若预设值大于默认的最大值检查是否溢出
if (var3 - 2147483639 > 0) {
var3 = hugeCapacity(var1);
}
//调用Arrays.copyOf方法将elementData数组指向新的长度为扩容后长度的内存空间
this.elementData = Arrays.copyOf(this.elementData, var3);
}
结论2:
集合初始化后,再次调用add方法,先将集合扩大1.5倍,如果仍然不够,新长度为传入集合大小。并调用Arrays.copyOf方法将elementData数组指向新的长度为扩容后长度的内存空间
总结
1.ArrayList创建对象时,若未指定集合大小初始化大小为0;若已指定大小,集合大小为指定的大小;
2.当第一次调用add方法时,集合长度变为10和addAll内容之间的较大值;
3.之后再调用add方法,先将集合扩大1.5倍,如果仍然不够,新长度为传入集合大小;