ArrayList底层就是一个可以grow扩容的数组Object[] elementData
1 成员属性
// 默认的数组大小
private static final int DEFAULT_CAPACITY = 10;
// 当没有设置初始化大小时候,所有实例的elementData共享指向
private static final Object[] EMPTY_ELEMENTDATA = {};
// 设置了初始化大小,但是设置为0时,所有实例的elementData共享指向
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // non-private to simplify nested class access
// 实际存储的元素大小,而不是elementData的长度
private int size;
2 主要方法
2.1增加元素
public boolean add(E e) {
// 判断是否需要扩容,如果需要则扩容数组
ensureCapacityInternal(size + 1);
// 就是底层数组对应元素指向e,size++既控制了数组index,又记录了当前元素总数
elementData[size++] = e;
return true;
}
// minCapacity = size + 1; 如果elementData.length <= size + 1,即数组实际长度不足 size + 1时候,就会触发扩容
// 很简单道理:数组长度至少要保证比size多1,才能保证能插入到数组中
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) {
modCount++;
// elementData.length 没有大于 size + 1,那么就插入不到新元素,需要扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// oldCapacity 扩容为以前的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 将元素从老组数拷贝到扩容后的新数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
2.2 删除元素
src源数组(被拷贝的数组)
srcPos源数组拷贝的起始位置
dest目标数组(需要被更新的数组)
destPos目标数组被更新的起始位置
length源数组的长度
static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
2.2.1 根据index删除元素
public E remove(int index) {
// 根据index获取老的元素,删除返回老元素
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
// 把删除元素index之后的元素,全部向前平移一位,被删除index自然被覆盖了
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 清空原来数组的最后一位,利于垃圾回收
elementData[--size] = null; // clear to let GC do its work
}
2.2.2 根据对象删除元素
public boolean remove(Object o) {
// 无论o是否为null,都是循环遍历删除,根据定位到的index,去数组删除
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++)
// 非空对象采用equals()方法进行比较,所以注意重写equal,同样通过index删除
if (o.equals(elementData[index])) {
// 底层同样采用System.arraycopy(src[], srcPos, des[], desPos, length)进行组数覆盖
fastRemove(index);
return true;
}
}
return false;
}
2.3 modCount
在我迭代器迭代操作时候,ArrayList不允许被我迭代器之外的修改(add、remove等);检查核心:迭代器内部Itr的计数器expectedModCount和外部类ArrayList的modCount是否相等;
class ArrayLIst {
// 调用ArrayLIst的实例方法add,remove都会修改modCount的值
protected transient int modCount = 0;
// 因为ArrayLIst实现Iterable接口,要求返回iterator迭代器
public Iterator<E> iterator() {
return new Itr();
}
// ArrayLIst内部自己定义一个迭代器类Iterator
private class Itr implements Iterator<E> {
// 内部类可以直接引用外部类的实例属性,且由于是简单类型,所以创建内部类时候有一份独立拷贝
// 这个独立拷贝在迭代时候可以和外部比较是否修改过
int expectedModCount = modCount;
Itr(){}
public E next() {
。。。
// 迭代过程检查是否修改
checkForComodification();
。。。
}
public void remove() {
。。。
checkForComodification();
// 调用外部实例的方法,会修改外部的modCount
ArrayList.this.remove(lastRet);
// 如果是迭代器自身修改的ArrayList,会同步外部最新modCount到expectedModCount ,所以迭代器自己检查时候不 // 会引发检查错误
expectedModCount = modCount;
。。。
}
final void checkForComodification() {
// 发现内部expectedModCoun和外部modCount 不同时,会触发异常
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
对于迭代器引发ConcurrentModificationException的总结:
为了维护保护迭代器在迭代过程中能够完整遍历所有数据(保证底层数组没有修改过);无论是同一个线程中对实例list使用list.remove();还是其他线程使用list.remove()或者iterator.remove()都会导致modCount != expectedModCount触发异常
2.4 在内部类中调用外部类的实例属性ArrayList.this.elementData和方法ArrayList.this.remove()
// 外部类
public class ArrayList{
// 外部类属性
transient Object[] elementData;
// 外部类方法
public E remove(int index) {......}
// 内部类
private class Itr implements Iterator<E> {
public E next() {
// 内部类中调用外部类的实例属性
Object[] elementData = ArrayList.this.elementData;
}
public void remove() {
// 内部类中调用外部类的实例方法
ArrayList.this.remove(lastRet);
}
}
}