阅读指引:本文包含源码,如果不想阅读源码,建议跳过 “ArrayList 的主要方法” 中的源码分析部分,直接看每一部分的小总结。
ArrayList是 Java 集合框架中比较常用的数据结构,底层基于数组实现,能够实现容量的动态变化。
我们之所以使用它,与它能够自由添加、删除元素有关,使用者不用考虑数组的容量、遍历等问题,可以说是一个功能强大、使用简单的数组。
那么ArrayList里面到底是怎么实现的呢?
了解一个类,我们需要先了解它有哪些变量,通过这些成员变量我们能够大致了解它的实现原理。
ArrayList 的主要变量:
private static final int DEFAULT_CAPACITY = 10; //数组初始容量
transient Object[] elementData; //存放数组
private int size; //实际元素个数,指已经使用的空间数
除此之外,ArrayList 的父类也有一个重要的变量:
protected transient int modCount = 0; //记录对 List 操作的次数
看到这里,我们不着急看具体方法,让我来猜想一下 ArrayList 主要方法的实现:
add(E e)
:在数组末尾添加元素。若数组为空,则初始化数组,容量为10;若数组已满,则扩容为原来的1.5倍。get(int index)
:返回指定位置的元素。若index越界,报错。remove(Object o)
:删除第一次出现对象为 o 的元素。若 o 为 null,查找并删除值为 null 的元素;若 o 不为 null,查找并删除 o 。size()
:返回ArrayList 中已有元素数量。返回 size。----------------------------------------------------下面是源码,慎入----------------------------------------------------------
接下来将从源码入手,分析 ArrayList 的主要方法是怎么实现的,可以先深吸一口气,我也将用比较简单的话来描述实现的过程。
有多种不同参数的 add() 方法,我们分析一下比较常用的add(E e):
注:代码中的中文注释是笔者添加的
public boolean add(E e) {
// 检查内部容量是否充足,如果不足则扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将元素存入数组
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
// 如果初始数组为默认空数组,minCapacity = 10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 检查是否需要扩容
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 添加新元素后数组长度 大于 数组当前容量
if (minCapacity - elementData.length > 0)
// 数组扩容,扩展为原来的 1.5 倍
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 相当于 newCapacity = oldCapacity + oldCapacity / 2
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
总结:
调用 add(E e) 方法,首先判断 elementData 是否为空数组,如果是空数组,初始化 elementData 容量为 10;如果不是空数组,则检查 elementData 数组是否已满,如果已满则进行扩容,扩展为原来的 1.5 倍。最后将元素 e 添加到 elementData 数组末尾。
该方法将 返回此列表中指定位置的元素。
public E get(int index) {
// 数组下标越界检查
rangeCheck(index);
return elementData(index);
}
private void rangeCheck(int index) {
// 如果下标 大于 数组元素数量
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
@SuppressWarnings("unchecked")
E elementData(int index) {
// 返回该元素
return (E) elementData[index];
}
总结
调用 get(int index) 方法,首先判断下标 index 是否越界,越界则抛异常,没越界则直接返回对应下标的元素。
该方法 将删除列表中第一次出现的指定元素。
public boolean remove(Object o) {
// 如果 o 为空
if (o == null) {
// 遍历 elementData, 找出 值为 null 的元素并删除
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
// 如果 o 不为空
} else {
// 遍历 elementData, 找出 值为 o 的元素并删除
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
// 删除指定下标元素
// 让 index 后的元素整体往前 copy, 然后旧数组的最后一个元素位置赋值为 null
private void fastRemove(int index) {
modCount++;
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
}
总结
调用 remove(Object o)方法,需要先判断 o 是否为 null,为空则遍历 elementData,找出值为 null 的元素并删除;如果不为空,遍历 elementData,找出值为 o 的元素并删除。
结束语:每天进步一点点~