基于jdk1.8版本
private static final int DEFAULT_CAPACITY = 10;
private int size;
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //最大容量值2^31-9
这两个变量值我们需要区分开,一个是ArrayList的容量capacity,一个是ArrayList盛装的元素的数量
transient Object[] elementData; // transient 表名当前对象不参与序列化
上面所说的size即便是elementData的size
// 如果使用 0 作为初始化容量的时候,ArrayList就会使用这个对象来初始化
private static final Object[] EMPTY_ELEMENTDATA = {};
// 如果使用默认的构造函数的话,就会使用这个对象初始化
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 直接使用DEFAULTCAPACITY_EMPTY_ELEMENTDATA来初始化
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 当initialCapacity 为0的时候,则会使用EMPTY_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);
}
}
public ArrayList(Collection extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
// 说实话上面的注释以及下面的判断我没有看懂
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
我们先自己分析一下思路(本来想画一个流程图的,但是后来发现不好命名,所以这个只好自己描述一下),首先只有在往容器里面添加数据的时候才会有扩容这一说,首先由于我们初始化的方法不同,所以在初次添加元素的时候需要做特别的处理,然后判断是否超过最大容量,判断在现有的size+1的基础上是否需要扩容,然后扩容,拷贝数组添加元素。
private void ensureCapacityInternal(int minCapacity) {
/** 如果使用默认的构造参数的话,则选择minCapacity和DEFAULT_CAPACITY中较大的一个来作为最小的初始容量,
大家可能奇怪,既然判断了初始化这一说,为何还会有传入的minCapacity
和DEFAULT_CAPACITY比较这一说,是因为ensureCapacityInternal也会被addAll调用
*/
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
// 下面这个完全是基于 **溢出来考虑的**
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
核心的扩容代码:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
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);
}
上面的两面不难,大家应该能看懂,不过上面确实有两个考点:
- 扩容后的新容量:为原来容量的1.5倍
- x>>y 和 x<
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
public E remove(int index) {
// 判断索引是否越界
rangeCheck(index);
modCount++;
// 获取被移除的元素
E oldValue = elementData(index);
// 需要移动的元素的个数
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
return oldValue;
}
图示如下:
还有很多方法,这里不再一一介绍,因为其他的方法比较常规,所以直接看api文档即可,下面重点来介绍一下一个异常和fast fail的故事。
先看异常:ConcurrentModificationException
modCount:list每进行一次cud操作,modCount就会+1,而在进行遍历的时候则会把这个值复制给expectedModCount ,在遍历的过程中一但这两个值不相等则说明了在遍历的过程中,有另外的线程对list做了修改(添加、删除、修改)这样出现数据不一致的情况是不允许的,所以抛出ConcurrentModificationException
那如果我不使用Iterator遍历是不是就不会出现这个情况呢,是的如果我们使用for循环就不会出现这个事情,但是也有一点小技巧需要注意,如果我们在for循环中遍历的同时删除数据,arraylist的size就会发生改变
for(int i = 0 ; isize();i++) // 不会出现问题
int size = list.size();
for(int i = 0 ; i<size ;i++) // 这种则会报空指针异常
// 这个是ArrayList的一个内部类,用来快速的遍历list的
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
现在我们基本上了解了ConcurrentModificationException,下面聊聊fast fail机制:其实ConcurrentModificationException是fast fail的一种响应,当Collection在遍历的过程中发现了修改就可能产生fast-fail,fast fail 机制并不保证在不同步代码的情况下一定会抛出异常,只是进最大努力抛出。注意这个机制对大多数的Collection都是有效的
解决的方法:
fast safe 和fast fail则是相对应的,可能这个地方我并没有介绍的太清楚,这个点在面试的过程中可能会被问到,做一了解即可,详细的介绍可以看一下这一篇博文https://www.cnblogs.com/shamo89/p/6685216.html