1.ArrayList
概述
ArrayList
继承自AbstractList
,实现了List
接口,底层基于数组实现了动态的扩容机制,在物理内存上采用顺序存储结构,即数组。因此可根据索引快速的查找元素,还具有基于索引操作元素的一套方法,允许null
元素的存在- 同时还实现了
RandomAccess
标志性接口,这意味着这个集合支持 快速随机访问策略,那么使用传统for
循环的方式遍历数据会优于用迭代器遍历数据,即使用get()
方法获取数据相比于迭代器遍历更加快速 ArrayList
还实现了Cloneable、Serializable
两个标志性接口,所以ArrayList
支持克隆、序列化
2. ArrayList
源码解读
2.1. 主要类属性
// 如果不指定容量(空构造器),则在添加数据时的空构造器默认初始容量最小为 10
private static final int DEFAULT_CAPACITY = 10;
// 出现在需要用到空数组的地方,其中一处是使用自定义初始容量构造方法时候如果你指定初始容量为0的时候,那么elementData指向该数组。另一处是使用包含指定collection集合元素的列表的构造方法时,如果被包含的列表中没有数据,那么elementData指向该数组
private static final Object[] EMPTY_ELEMENTDATA = {
};
// 如果使用默认构造方法,那么elementData指向该数组。在添加元素时会判断是否是使用默认构造器第一次添加,如果是数组就会扩容至10个容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
};
// 默认未初始化的储存 ArrayList集合元素的底层数组,其长度就是 ArrayList的容量
transient Object[] elementData;
// 私有的elementData数组中具体的元素对象的数量,可通过size方法获得。默认初始值为0,在add、remove等方法时size会改变
private int size;
2.2. 构造器与初始化容量
2.2.1. 默认的构造器 ArrayList()
public ArrayList() {
// 如果使用默认构造方法,那么 elementData 指向该数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
使用空构造器时,其实当我们第一次为 ArrayList
添加元素的时候,底层数组扩容到了至少 10
。这在动态扩容机制中会讲到
2.3. add()
方法与动态扩容机制(重点)
ArrayList
不像数组那样,它能屈能伸,所以它实现了动态的扩容。一旦在添加元素的时候,发现容量用满了即minCapacity - elementData.length > 0 = false
时,就按照原来数组的1.5
倍(oldCapacity >> 1
)进行扩容,扩容之后,再将原有的数组复制到新分配的内存地址上Arrays.copyOf(elementData, newCapacity)
- 触发
ArrayList
动态扩容的条件是minCapacity - elementData.length > 0
是否为true
。为true
则触发;反之,不会触发
public boolean add(E e) {
// 判断并进行数组的扩容 or 长度超限的方法
ensureCapacityInternal(size + 1);
// 为 size的所在索引赋值,并且 size 自增 1
elementData[size++] = e;
return true;
}
size
是当前集合拥有的元素个数,不是容量大小,且未算进准备新增的元素size + 1
是保证新进入的元素能满足要求
// minCapacity 最小容量,此时 minCapacity = size + 1
private void ensureCapacityInternal(int minCapacity) {
// 如果是采用空构造器初始化集合,并且第一次添加元素
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// minCapacity 设置为 DEFAULT_CAPACITY 和 minCapacity 的最大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 走下一个方法,判断是否需要扩容 or 判断数组长度是否溢出
ensureExplicitCapacity(minCapacity);
}
/**
* 判断是否需要走扩容or判断数组长度是否可能溢出
* @param minCapacity 最小容量
*/
private void ensureExplicitCapacity(int minCapacity) {