ArrayList源码学习
对于ArrayList我们都很熟悉,使用起来非常的方便,使用的较多的方法有add(),remove(),indexOf()等,对于这种优秀的集合框架,研究其源码能让我们对其掌握更加深刻,能更合理的应用在业务场景中,同时我们自己在写程序时也能够参考其设计思想,提供我们的编码水平。
一.ArrayList简介
1.ArrayList底层是数组队列,相当于是动态数组,即它是基于数组实现的一个List类。
2.该类封装了一个Object[]数组,capacity属性代表此数组的长度,通过ensureCapacity方法可以一次性增加capacity,能减少增加重分配的次数来提高性能。
3.类的继承结构和层次关系,如下图:
如图
继承了AbstracList抽象类,实现了List接口。List中定义上层接口,AbstracList对一些通用方法进行了实现,ArrayList对自己特有的方法再进行实现。
实现了RandomAccess接口,RandomAccess接口是一个标志接口,表明实现这个接口的List集合是支持快速随机访问的。
实现了Cloneable接口,即覆盖了Clone()函数,能被克隆。
实现了Serializable接口,表明其能被序列化,能通过序列化去传输。
另ArraList是非线程安全的,想保证线程安全可以使用Vector或CopyOnWriteArrayList。
二.源码解析
1.属性和构造方法:
/**
* 版本号
*/
private static final long serialVersionUID = 8683452581122892189L;
/**
* 默认初始容量大小
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空数组(用于空实例)。
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
//用于默认大小空实例的共享空数组实例。
//我们把它从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量需要增加多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 保存ArrayList数据的数组
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* ArrayList 所包含的元素个数
*/
private int size;
/**
* 带初始容量参数的构造函数。(用户自己指定容量)
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
//创建initialCapacity大小的数组
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
//创建空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
*默认构造函数,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 为0.初始化为10,也就是说初始其实是空数
*组 当添加第一个元素的时候数组容量才变成10
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
*/
public ArrayList(Collection<? extends E> c) {
//
elementData = c.toArray();
//如果指定集合元素个数不为0
if ((size = elementData.length) != 0) {
// c.toArray 可能返回的不是Object类型的数组所以加上下面的语句用于判断,
//这里用到了反射里面的getClass()方法
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 用空数组代替
this.elementData = EMPTY_ELEMENTDATA;
}
}
2.核心方法
2.1 add()方法
boolean add(E e)方法源码分析
//在list末尾添加指定元素
public boolean add(E e) {
//确定list容量是否足够,size+1是因为新添加了一个元素
ensureCapacityInternal(size + 1); // Increments modCount!!
//确保容量充足后size++,再将新元素放到size++位置
elementData[size++] = e;
return true;
}
//确定容量是否充足的方法
private void ensureCapacityInternal(int minCapacity) {
//看elementData数组是否是空数组,如果是,minCapacity这里应该等于size+1,即1
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//DEFAULT_CAPACITY=10,所以这里实际上就是让minCapacity=10,也就是初始化大小
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//确认数组容量,接着往下分析
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//minCapacity如果大于实际elementData的长度,则说明容量是不够用的,需要扩容
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//真正进行扩容的方法,还得接着看
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
//扩容前elementData的长度
int oldCapacity = elementData.length;
//扩容后的长度为原先长度的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//这里的判断是因为elementData最初为空数组时,newCapacity为0,而minCapacity为10,可以
//看出就是为了最初初始化数组
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//当newCapacity超过了最大值,则调用hugeCapacity方法,将能给的最大值赋予newCapacity
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);
}
void add(int index, E element)源码分析
//在特定位置插入元素
public void add(int index, E element) {
//检查插入的位置是否合理
rangeCheckForAdd(index);
//和上面的分析一样
ensureCapacityInternal(size + 1); // Increments modCount!!
//此方法用于插入元素之后,要将index之后的元素全部后移一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将元素放置在index位置
elementData[index] = element;
size++;
}
/**
* A version of rangeCheck used by add and addAll.
*/
private void rangeCheckForAdd(int index) {
//如果index不在正常范围内则抛出异常
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
2.2 remove()方法
remove(int index)方法
//删除指定位置上的元素
public E remove(int index) {
//检查index范围是否合理
rangeCheck(index);
//用来检查快速失败的一个标志
modCount++;
//直接通过下标找到元素
E oldValue = elementData(index);
//计算要移动多少个元素
int numMoved = size - index - 1;
if (numMoved > 0)
//用来移动元素的具体方法
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//将--size上的元素赋为null,让gc更快的回收
elementData[--size] = null; // clear to let GC do its work
//返回删除的元素
return oldValue;
}
remove(Object o)方法
//通过传入的元素来删除list中的此元素,原理就是遍历数组,查找到此元素后将其索引传给fastRemove
//方法,其内部实现与remove(int index)基本一致
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
removeAll(Collection<?> c)方法
//批量删除
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
//主要看batchRemove方法的实现
return batchRemove(c, false);
}
//这个方法,用于两处地方,如果complement为false,则用于removeAll,如果为true,则给
retainAll()用,retainAll()是用来检测两个集合是否有交集的。
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData; //将原集合,记名为A
int r = 0, w = 0; //r用来控制循环,w是记录有多少个交集
boolean modified = false;
try {
for (; r < size; r++)
//参数中的集合C检测是否包含集合A中的元素
if (c.contains(elementData[r]) == complement)
//有的话,就给集合A
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
//如果contains方法使用过程报异常
if (r != size) {
//将剩下的元素都赋值给集合A,
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
//这里有两个用途,在removeAll()时,w一直为0,就直接跟clear一样,全是为null。
//retainAll():没有一个交集返回true,有交集但不全交也返回true,而两个集合相等的时候,返回
false,所以不能根据返回值来确认两个集合是否有交集,而是通过原集合的大小是否发生改变来判断,如
果原集合中还有元素,则代表有交集,而元集合没有元素了,说明两个集合没有交集。
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
2.3 set()方法
public E set(int index, E element) {
//检查index范围是否正常
rangeCheck(index);
//获取旧值
E oldValue = elementData(index);
//赋新值
elementData[index] = element;
//返回旧值
return oldValue;
}
2.4 indexOf()方法
public int indexOf(Object o) {
if (o == null) {
//如果元素为null,则遍历数组,找到第一个为null的元素,返回其下标
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
//如果元素不为null,则遍历数组,找到第一个和指定元素相等的元素,返回其下标
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
//如果元素不存在则返回-1
return -1;
}
2.4 get()方法
public E get(int index) {
//检查index范围是否正常
rangeCheck(index);
//根据数组下标返回元素
return elementData(index);
}