今天来看一下ArrayList的源码java
通常来说文章开始应该先介绍一下说下简介。这里就不介绍了 若是你不知道ArrayList是什么的话就不必在看了。大体讲一下一些经常使用的方法数组
ArrayList源码定义:安全
ArrayList继承结构以下:多线程
Serializable 序列化接口并发
Cloneable 前面咱们在看Object源码中有提到这个类,主要表示能够进行克隆dom
List 主要定义了一些方法实现ide
RandomAccess 也是一个标记类,实现RandomAccess表示该类支持快速随机访问。ui
ArrayList中的主要属性方法有:this
初始化容量,默认为10spa
private static final int DEFAULT_CAPACITY = 10;
复制代码
空数组
private static final Object[] EMPTY_ELEMENTDATA = {};
复制代码
也是一个空数组,和上面的数组相比主要的做用是在添加元素的时候知道数组膨胀了多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
复制代码
ArrayList的大小
private int size;
复制代码
注意ArrrayList中有一个modCount成员变量,来记录修改次数,主要是在使用迭代器遍历的时候,用来检查列表中的元素是否发生结构性变化(列表元素数量发生改变)了,主要在多线程环境下须要使用,防止一个线程正在迭代遍历,另外一个线程修改了这个列表的结构。好好认识下这个异常:ConcurrentModificationException。对了,ArrayList是非线程安全的。
咱们来看一下ArrayList的构造方法 无参构造方法:
能够看出无参构造会直接建立一个指定DEFAULTCAPACITY_EMPTY_ELEMENTDATA的数组,而DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组默认是空的。
因此在执行一下代码的时候ArrayList默认建立的是一个初始化容量为0 的数组。
ArrayList list = new ArrayList();
复制代码
有参构造方法:
传入建立数组的大小,若是大于0 就建立一个传入参数大小的数组,若是等于0 就就指定为空数组。若是小于0就会抛异常。
看源码咱们能够看到传入Collection的构造方法作的事情就是复制数组,将已有的集合复制到新的集合中。
ArrayList底层是用数组来实现的,那咱们就一块儿来看如下Add方法是如何实现的
如下是Add方法的实现源码。
能够看到ArrayList在添加元素以前先检查一下集合的大小
在 ensureExplicitCapacity 方法中,首先对修改次数modCount加一,这里的modCount给ArrayList的迭代器使用的,在并发操做被修改时,提供快速失败行为(保证modCount在迭代期间不变,不然抛出ConcurrentModificationException异常,能够查看源码865行),接着判断minCapacity是否大于当前ArrayList内部数组长度,大于的话调用grow方法对内部数组elementData扩容,grow方法代码以下:
ArrayList的删除操做一共有两种一种是根据索引删除,一种是根据内容删除。
根据索引删除元素
咱们能够看到首先判断一下是否为空。不为空的话就开始循环查找元素,用equals来判断元素是否相同,若是一致就调用fastRemove来删除元素。而后经过System.arraycopy进行自身复制。
由于自己ArrayList就是用数组来实现的,因此获取元素就相对的来讲简单一点。
直接返回size==0的结果,是否是很是简单。
for循环可能在java中是最经常使用的遍历方法主要实现:
先看实现:
/** * An optimized version of AbstractList.Itr */
private class Itr implements Iterator<E> {
//游标, 下一个要返回的元素的索引
int cursor;
// 返回最后一个元素的索引; 若是没有这样的话返回-1.
int lastRet = -1;
int expectedModCount = modCount;
Itr() {}
//经过 cursor != size 判断是否还有下一个元素
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
//迭代器进行元素迭代时同时进行增长和删除操做,会抛出异常
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
//游标向后移动一位
cursor = i + 1;
//返回索引为i处的元素,并将 lastRet赋值为i
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//调用ArrayList的remove方法删除元素
ArrayList.this.remove(lastRet);
//游标指向删除元素的位置,原本是lastRet+1的,这里删除一个元素,而后游标就不变了
cursor = lastRet;
//lastRet恢复默认值-1
lastRet = -1;
//expectedModCount值和modCount同步,由于进行add和remove操做,modCount会加1
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
//前面在新增元素add() 和 删除元素 remove() 时,咱们能够看到 modCount++。修改set() 是没有的
final void checkForComodification() {
if (modCount != expectedModCount)
//也就是说不能在迭代器进行元素迭代时进行增长和删除操做,不然抛出异常
throw new ConcurrentModificationException();
}
}
复制代码
从上面的代码咱们得出,在遍历的时候若是删除或者新增元素都会抛异常出来,而修改不会。看下方例子。
小弟不才,若有错误请指出。喜欢请关注,慢慢更新JDK源码阅读笔记