1.ArrayList基本原理:ArrayList底层基于数组实现,所有插入是有顺序的,数组是一块两块空间,引用它的首地址,长度是固定的,添加元素数组长度满了的时候就会扩容,创建一个新的数组把元素都复制过去,如果在中间插入或删除会导致数组元素一个一个移动,数组有下标随机查找快。
能预估数据长度的条件下,尽量指定长度,避免长度小频繁扩容,适用场景遍历多,随机查找多,没有频繁插入、随机插入少、随机删除少的场景
2.成员变量分析
/**
* 默认初始长度
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 空元素数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 默认长度的空元素数组
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 实际存放元素的Object[]
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* 实际元素数量
*/
private int size;
3.构造方法源码分析
/**
* 默认无参构造方法,把默认长度的空元素数组赋值给Object[] elementData ,也就是{}
* 但是构造方法执行完以后elementData 的长度还是0,第一次set的时候会扩容为DEFAULT_CAPACITY 也就是10
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 指定长度的构造方法,如果initialCapacity大于0就创建一个initialCapacity长度的Object数组赋值给elementData
* 如果initialCapacity为0,就把EMPTY_ELEMENTDATA赋值给elementData ,也就是{}
* 如果小于0就会抛出IllegalArgumentException异常
*/
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);
}
}
/**
* 传入一个集合,toArray方法是Arrays.copyOf(elementData, size)
* 通过Arrays.copyOf将传入集合的元素数组复制到elementData 中
* 如果传入集合有元素并且不是object[]类型就复制到object[]数组中赋值给elementData
* 如果传入集合的长度为0,就把EMPTY_ELEMENTDATA赋值给elementData ,也就是{}
*/
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 {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
4.boolean add(E e)源码分析
/**
* 在末尾添加一个元素,每次添加的时候先判断一下长度是否超过elementData的长度,如果超过就扩容
* 然后将新添加的元素放到size位置也就是最后一个元素的下一个位置,size自增1
*/
public boolean add(E e) {
// ensureCapacityInternal涉及到元素扩容,后面单独分析
ensureCapacityInternal(size + 1);
// 把元素放到数组的size位置,然后size++,假如第一次添加元素e放到第0个位置,然后size变成1
elementData[size++] = e;
return true;
}
5.void add(int index, E element)源码分析
/**
* 在指定位置插入一个元素
* 先判断指定的位置是否在0到size之间,如果不在就抛出IndexOutOfBoundsException异常,也就是数组越界
* 然后把指定位置后面所有的元素都往数组后面挪1
* 然后把新元素设置到指定的index位置
* 然后把size加1
*/
public void add(int index, E element) {
// 下面贴出了rangeCheckForAdd代码,校验index是不是在0到size之间,不在抛出数据越界异常
rangeCheckForAdd(index); // Increments modCount!!
// ensureCapacityInternal涉及到元素扩容,后面单独分析
ensureCapacityInternal(size + 1); // Increments modCount!!
// 通过System.arraycopy方法把数组指定位置的元素往后挪1
// 将elementData从index位置开始复制到elementData,从index + 1开始复制size - index个
// 也就是将原来元素数组从指定位置开始复制到自己,从指定位置下一个开始,复制后面所有的元素,实现全部往后面挪1的效果
// 举例:移动前["张三","李四","王五"],add(1, "赵六")后,["张三","李四","李四","王五"]
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// 将指定位置设置为新元素,add(1, "赵六")后变成["张三","赵六","李四","王五"]
elementData[index] = element;
size++;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
6.set(int index, E element)源码分析
/**
* 在指定位置设置一个值,覆盖效果,返回旧值
*/
public E set(int index, E element) {
// 校验index是不是在0到size之间,不在抛出数据越界异常
rangeCheck(index);
// 下面贴出了elementData(index)的代码,就是取出index位置的元素
E oldValue = elementData(index);
// 将新元素设置到指定index位置
elementData[index] = element;
// 返回旧值
return oldValue;
}
E elementData(int index) {
return (E) elementData[index];
}
7.E get(int index)源码分析
/**
* 取出指定位置的元素
*/
public E get(int index) {
// 校验index是不是在0到size之间,不在抛出数据越界异常
rangeCheck(index);
// 返回指定index位置的元素
return elementData(index);
}
8.E remove(int index)源码分析
/**
* 删除指定位置的元素,返回被删除的元素
* 如果删除的元素不是最后一个元素,那么被删除位置的元素都要往前面挪1
*/
public E remove(int index) {
// 校验index是不是在0到size之间,不在抛出数据越界异常
rangeCheck(index);
// modCount涉及到遍历相关,fail-fast机制,后面会分析
modCount++;
E oldValue = elementData(index);
// 假如size为10,要删除下标为9的元素,10-9-1==0,如果numMoved==0说明删除的是最后一个元素
int numMoved = size - index - 1;
// 不是最后一个元素,那么利用System.arraycopy方法,被删除位置的元素都要往前面挪1
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 将最后一个元素位置为null,size减一
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
9.扩容机制,ensureCapacityInternal源码分析
// 调用ensureCapacityInternal(size + 1)
private void ensureCapacityInternal(int minCapacity) {
// calculateCapacity(elementData, minCapacity)默认构造方法以后第一次add返回10,否则都是返回minCapacity
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// minCapacity超过了原来数组长度就扩容
private void ensureExplicitCapacity(int minCapacity) {
// modCount涉及到遍历相关,fail-fast机制,后面会分析
modCount++;
// overflow-conscious code
// 如果minCapacity 比elementData.length要大说明超过了原来的长度
if (minCapacity - elementData.length > 0)
// 进行扩容,创建新长度的数组,然后把原来数组元素复制过去
grow(minCapacity);
}
// 如果新创建的ArrayList那么elementData初始值是DEFAULTCAPACITY_EMPTY_ELEMENTDATA
// elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA,也就是默认构造方法创建好ArrayList以后第一次调用add方法
// minCapacity为1,取DEFAULT_CAPACITY 10,然后10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
/**
* 创建新长度的数组,然后把原来数组元素复制过去
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// oldCapacity >> 1是除2的意思,也就是新长度是原来的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新长度比原来minCapacity小,长度还是minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新长度比MAX_ARRAY_SIZE 大,MAX_ARRAY_SIZE 是Integer.MAX_VALUE - 8,则长度为Integer.MAX_VALUE
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 利用Arrays.copyOf方法,将elementData中的元素都复制到newCapacity中去
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
10.RandomAccess接口
ArrayList实现了RandomAccess接口,RandomAccess没有实现任何操作,RandomAccess只是一个标识作用,标识这个集合随机读快,我们可以利用instanceof来判断哪一个是实现了RandomAccess。分辨出两个集合。其中ArrayList使用for循环遍历快,而LinkedList使用迭代器快。那么通过分辨,不同的集合使用不同的遍历方式。
11.fail-fast机制
fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出ConcurrentModificationException异常,产生fail-fast事件。
前面分析源码的时候多处都有modCount++,我们看看循环迭代的代码
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
// 拿到modCount赋值给expectedModCount
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
// 如果循环迭代的时候,其他线程修改了modCount,就会导致modCount和expectedModCount不一致
// 就会抛出ConcurrentModificationException异常,所以循环的时候不能删除某个元素
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
其他Collection集合也有fail-fast机制