ArrayList源码解析
简介
ArrayList是一个基于数组来实现动态扩容的集合,具有良好的查询性能。
1、重要的成员变量
// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
// 空数组,有参构造参数为0时,即创建一个容量为0的list时使用
private static final Object[] EMPTY_ELEMENTDATA = {};
// 空数组,无参构造时使用
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 数组
transient Object[] elementData;
// 数组大小
private int size;
2、构造方法
使用有参构造时,如果初始容量大于0,则直接创建数组;如果初始容量等于0,则使用EMPTY_ELEMENTDATA空数组。
// 无参构造函数
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 有参构造函数
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);
}
}
3、核心方法
3.1 插入元素 add(E e)
最常用的方法之一,每次添加元素前先判断容量是否足够,不够则触发扩容。
public boolean add(E e) {
// 判断list容量是否够,不够则扩容。
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
3.2 容量大小判断
先计算所需要的最小容量,再判断是否需要扩容。
如果是无参构造创建的list,则直接用默认初始容量和最小所需容量比较获取较大者。因此,如果创建list的时候没有指定大小,添加元素时list会默认扩容至10。
如果最小容量大于数组的容量,则触发扩容。
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 计算所需要的最小容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果是无参构造创建的list,则直接用默认初始容量和最小所需容量比较获取较大者。
// 即如果创建list的时候没有指定大小,添加元素时list会默认扩容至10。
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
// 如果所需最小容量大于list的容量,则需要扩容。
grow(minCapacity);
}
3.3 扩容方法 grow()
新容量为旧容量加上旧容量的一半,即扩容为原来的1.5倍。
扩容完成后,将旧数组的数据拷贝到新数组中,但会影响性能。所以如果能确定一个list的大小,最好使用在创建时指定大小。
// 开始扩容
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 新容量为旧容量加上旧容量的一半,即扩容为原来的1.5倍。
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 将数据拷贝到新数组中,这里会损失性能。
// 所以如果能确定一个list的大小,最好使用在创建时指定大小。
elementData = Arrays.copyOf(elementData, newCapacity);
}
3.4 获取List中元素 get()
这里会做一次检查,判断是否越界。
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}