ArrayList源码解析
简介:
ArrayList 是 java 集合框架中比较常用的数据结构了。继承自 AbstractList,实现了 List 接口。底层基于数组实现容量大小动态变化。允许 null 的存在。同时还实现了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 是支持快速访问、复制、序列化的。
1、成员属性介绍
ArrayList 底层是基于数组来实现容量大小动态变化的。
// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
// 用于空实例的共享空数组实例。
private static final Object[] EMPTY_ELEMENTDATA = {};
// 用于默认大小的空实例的共享空数组实例。 我们将其与 EMPTY_ELEMENTDATA 区分开来,以了解添加第一个元素时要膨胀多少。简单来讲就是第一次添加元素时知道该 elementData 从空的构造函数还是有参构造函数被初始化的。以便确认如何扩容。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// ArrayList 的元素存储在其中的数组缓冲区。 ArrayList 的容量就是这个数组缓冲区的长度。 添加第一个元素时,任何带有 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的空 ArrayList 都将扩展为 DEFAULT_CAPACITY。
transient Object[] elementData;
// ArrayList 的大小(它包含的元素数)。size 是指 elementData 中实际有多少个元素,而 elementData.length 为集合容量,表示最多可以容纳多少个元素。
private int size;
// 要分配的数组的最大大小。
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 这个变量是定义在 AbstractList 中的。记录对 List 操作的次数。主要使用是在 Iterator,是防止在迭代的过程中集合被修改。
protected transient int modCount = 0;
2、构造函数
// 构造一个初始容量为 10 的空列表,
// 注意:注释是说构造一个容量大小为 10 的空的 list 集合,但构造函数了只是给 elementData 赋值了一个空的数组,其实是在第一次添加元素时容量扩大至 10 的。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 构造一个具有指定初始容量的空列表。
// 也就是说:当使用无参构造函数时是把 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 赋值给 elementData。 当 initialCapacity 为零时则是把 EMPTY_ELEMENTDATA 赋值给 elementData。 当 initialCapacity 大于零时初始化一个大小为 initialCapacity 的 object 数组并赋值给 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);
}
}
// 按照集合的迭代器返回的顺序构造一个包含指定集合元素的列表
// 也就是说:使用指定 Collection 来构造 ArrayList 的构造函数
public ArrayList(Collection<? extends E> c) {
Object[] a = c.toArray();
if ((size = a.length) != 0) {
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
elementData = Arrays.copyOf(a, size, Object[].class);
}
} else {
// replace with empty array.
elementData = EMPTY_ELEMENTDATA;
}
}
3、add()方法
add(E e)新增元素
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
// 索引位赋值
elementData[size++] = e;
return true;
}
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;
}
private void ensureExplicitCapacity(int minCapacity) {
// list 发生改变的次数
modCount++;
// overflow-conscious code
// 如果元素个数大于数组长度则进行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 扩容的实现
private void grow(int minCapacity) {
// overflow-conscious code
// oldCapacity 为 当前数组的长度
int oldCapacity = elementData.length;
// newCapacity 为 当前数组的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 此条件成立说明是第一次添加元素,初始容量为10
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 此条件成立说明数组中的元素超出最大限制,这个在部分虚拟机容易内存溢出
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 旧的数组赋值到需要扩容大小的数组当中
elementData = Arrays.copyOf(elementData, newCapacity);
}
add(E e,int index) 在指定位置插入元素
// 在此列表中的指定位置插入指定元素。 将当前在该位置的元素(如果有)和任何后续元素向右移动(向它们的索引添加一个)。
public void add(int index, E element) {
// 检查 index是否大于0或者index是否小于 size 否则抛出数组越界异常
rangeCheckForAdd(index);
// 确保是否需要进行扩容 (上面已讲)
ensureCapacityInternal(size + 1); // Increments modCount!!
// 索引位以后的元素进行向后位移
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// //在索引位插入元素
elementData[index] = element;
size++;
}
4、remove()方法
// 移除此列表中指定位置的元素。 将任何后续元素向左移动(从它们的索引中减去一个)
public E remove(int index) {
// 此处的索引只能是 当前数组容量 -1 否则抛出 数组越界异常
rangeCheck(index);
modCount++;
// 获取当前数组需要移除元素的值
E oldValue = elementData(index);
// index 索引位后面的元素个数
int numMoved = size - index - 1;
// 此条件成立说明,当前索引位后面的元素都需要向左移动一位
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 清除size-1索引位的元素,并进行-1
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
5、get()方法
// 返回当前数组中指定索引位的元素
public E get(int index) {
// 检查 index 是否是 小于 数组越界异常(此方法不会检查负数)
rangeCheck(index);
// 获得当前索引位的元素
return elementData(index);
}