首先,来总览一下 ArrayList 的类结构图。
接着,我们来看一下 ArrayList 的属性:
private static final int DEFAULT_CAPACITY = 10; // 初始化容量为10
private static final Object[] EMPTY_ELEMENTDATA = {}; // 指定该ArrayList容量为0时,返回该空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 没有指定容量时,默认返回该数组
transient Object[] elementData; // 保存添加到ArrayList中的元素(当第一次添加元素时,将扩容到 DEFAULT_CAPACITY)
private int size; // ArrayList实际的大小
根据上面我们可以清晰的发现:ArrayList 底层其实就是一个数组,ArrayList 中有扩容这么一个概念,正因为它扩容,所以它能够实现动态增长。
一、构造方法
public ArrayList(int initialCapacity) {
// 如果指定了容量,那么数组就初始化成对应的容量
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 容量为0,则将 elementData 指向事先定义好,容量为0数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
// 不传入参数,则将 elementData 指向默认容量为10的数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
二、add 方法
2.1 add(E e)
首先,我们来看一下这个方法:
public boolean add(E e) {
// 确认list容量,尝试容量加1,看看是否有必要扩容
ensureCapacityInternal(size + 1);
// 添加元素
elementData[size++] = e;
return true;
}
接下来,我们来看看这个小容量(+1)是否满足我们的需求:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 想要得到最小的容量(不浪费资源)
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
随后调用 ensureExplicitCapacity()
来确定明确的容量:
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
// 如果需要的最小容量比现在数组的容量还要大,那么就需要调用grow()来扩容了
grow(minCapacity);
}
所以,接下来看看 grow()
是怎么实现的:
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 相当于扩容1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
// 扩完容,如果还是小于minCapacity
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 调用copyOf()将原数组复制到新数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
目前为止,我们已经知道 add(E e)
的基本实现了:首先检查 elementData 数组容量是否足够,如果足够,则直接添加;如果不足,则需要扩容到原来的 1.5 倍(如果第一次扩容后数组容量还是小于 minCapacity,就将容量扩充为 minCapacity)
2.2 add(int index, E element)
public void add(int index, E element) {
rangeCheckForAdd(index);// 检查角标是否越界
ensureCapacityInternal(size + 1);// 确认是否需要扩容
System.arraycopy(elementData, index, elementData, index + 1,
size - index); // 插入元素
elementData[index] = element;
size++;
}
三、get 方法
public E get(int index) {
rangeCheck(index);// 检查角标
return elementData(index);// 返回元素
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
E elementData(int index) {
return (E) elementData[index];
}
四、set 方法
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);// 将值进行替代,返回旧值
elementData[index] = element;
return oldValue;
}
五、remove 方法
public E remove(int index) {
rangeCheck(index); // 检查角标
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1; // 需要向左移动的个数
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
六、细节再说明
- ArrayList 是基于动态数组实现的,在增删的时候,需要数组的拷贝复制
- ArrayList 的默认初始容量是10,每次扩容的时候增加原容量的一半,也就是变为原来的 1.5 倍
- 删除元素时不会减少容量,若希望减少容量则调用
trimToSize()
- 它不是线程安全的。它能存放 null 值