ArrayList继承了AbstractList,实现了List接口,底层实现基于数组,因此可以认为是一个可变长度的数组。
ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去
ArrayList 部分源码
/**
* 默认初始容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 定义一个空数组以供使用
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 也是一个空数组,构造无参构造方法时用到
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 存放数组中的元素,transient修饰,不被序列化
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* 表示数组中所包含实际元素的个数,和elementData.length不同
*/
private int size;
/**
* 记录list被修改的次数
*/
protected transient int modCount = 0;
/**
* 分配数组的最大大小,(有些虚拟机在数组中保留一些头词,如果 分配更大的数组可能导致 OutOfMemoryError:请求的数组大小超过虚拟机限制 )
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 无参构造函数,返回空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 有参构造函数,构造一个具有指定初始容量的空list,initialCapacity = 0 时 ,返回空数组EMPTY_ELEMENTDATA,与无参构造返回的 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 不同
* @param initialCapacity
*/
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);
}
}
/**
* 有参构造函数,构造包含指定元素的list
* @param c
*/
public ArrayList(Collection<? extends E> c) {
//将传入的集合转为数组
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray可能(不正确地)不返回Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
//通过Arrays.copyOf方法把集合中的元素拷贝到elementData中
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
/**
* 将指定元素追加到列表末尾
* @param e
* @return
*/
public boolean add(E e) {
int i =0;
//size + 1 把数组实际长度加1,以确保能保持下下一个数据
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
/**
* 将指定元素插入到指定的位置
* 将当前元素移动到该位置(如果有的话),并 移动右边的后续元素(在它们的下标上加1)
* @param index
* @param element
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
/**
* minCapacity = size + 1
* @param minCapacity
*/
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/**
* 计算容器大小
* 如果是调用了无参构造返回的空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),
* 则返回 默认初始容量(DEFAULT_CAPACITY=10) 和 minCapacity = size+1 中的最大值 ,由于第一次调用 size=0,故此时返回 10;
* 否则返回 minCapacity = size+1,注意只有第一次调用add时,才返回 1,此后的调用需要根据实际的数组长度size+1
* @return
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/**
* 判断是否需要调用扩容方法
* 1. 如果 new ArrayList 时 给定了长度(长度 > 0),即调用有参构造 ,那elementData.length 就是 传入的长度,
* 而第一次调用add 时,由于size=0, 所以 minCapacity = size +1 = 1, 故 不需要调用 扩容函数
* 2. 如果 new ArrayList 时 没有给定了长度,即调用无参构造,返回的空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA ,
* 那 elementData.length = 0 , 则满足条件,需要调用扩容方法
* @param minCapacity
*/
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//如果minCapacity > elementData.length ,即 当前数组实际长度 大于 数组容量时 ,调用扩容方法 grow()
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 扩容方法
* @param minCapacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //原来的容量
// 新容量 = 原容量右移一位+原容量 = 原容量*1.5
int newCapacity = oldCapacity + (oldCapacity >> 1);
//若新容量 < 数组实际长度+1 , 则 直接让 数据实际长度+1 为新容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//如果新容量 大于 分配数组的最大大小
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 通过Arrays.copyOf方法把原数组的内容放到更大容量的数组里面
elementData = Arrays.copyOf(elementData, newCapacity);
}
//如果数据实际长度+1 大于 分配数组的最大大小, 则返回 最大整数,否则 返回 分配数组的最大大小
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
假如我们放入11个值,那ArrayList的扩容过程是怎样?
ArrayList 初始化 elementData 数组长度为0,放入第一个值的时候,elementData 是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,所以第一次扩容是DEFAULT_CAPACITY,也就是10,所以当前elementData数组长度是10, 然后放入11个值,所需数组长度是12, 大于elementData 数组长度10,需要扩容,第二次扩容是按当前数组长度的一半,也就是5,所以扩容长度是15,15比所需长度12大,满足条件进行扩容,所以elementData 数组长度是15
总结:
调用add()方法 时 扩容步骤:
- 如果 放入的值个数小于10个,那 数组容量就是10;
- 如果 放入的值个数 大于10个 ,小于 15 个 ,则 容量为 10*1.5 = 15 ;
- 如果 放入的值 个数大于15 个,小于 22 个,则容量为 15*1.5 = 22
- ·····以此类推,每次递增到 原数组容量的1.5倍