1 前言
本人使用的是jdk1.8版本。
2 List集合继承结构
3 底层实现
可以看到底层是通过一个Object[] elementData来存储数据的,在不指定初始容量的情况下(空参构造)默认容量为10,不过要在第一次add操作时才会为elementData变量赋值(懒扩充)。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
// 最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 空参构造一个ArrayList时,elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA,在第一次添加
// 元素时将elementData 长度扩充到10(懒扩充)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 存放数据的仓库
transient Object[] elementData;
// ArrayList中已添加的元素的个数
private int size;
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
public ArrayList(int initialCapacity) { ...... }
}
4 基本操作和扩容方法
这里基本操作只选取了一种,实现中有多个重载方法。
4.1 add()
可以看到add方法首先会进行扩容判断,若添加成功结果返回true。调用成功modCount+1,modCount在下面会有介绍。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 扩容判断及实现
elementData[size++] = e;
return true;
}
4.2 remove()
调用成功modCount+1.
public E remove(int index) {
rangeCheck(index);
modCount++; // 修改次数+1
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; // 返回被删除的元素对象
}
4.3 set()
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
4.4 get()
get方法也十分简单,首先判断给定索引是否越界,否则返回指定位置的元素。
public E get(int index) {
rangeCheck(index); // 判断索引是否越界
return elementData(index);
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
4.5 扩容方法
ArrayList中的扩容方法是ensureCapacityInternal(),与StringBuilder的扩容机制很相近,可以参考:string、StringBuilder和StringBuffer。在ArrayList中,默认扩容机制是newCapacity = oldCapacity * 1.5。若默认扩容长度不够,则取elementData.length + addCount;若elementData.length + addCount为负数或者大于Integer.MAX_VALUE则会抛出OutOfMemoryError。从下面代码可知:elementData的长度最大可以取到Integer.MAX_VALUE,并不受到字段MAX_ARRAY_SIZE的限制,这里要注意一下。
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 空参构造则赋给elementData默认容量
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++; // 修改次数+1
// 若elementData不能满足容量要求,则进行扩容
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; // 若默认长度还不能满足需求,取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);
}
// 若minCapacity为负数,则抛出OutOfMemoryError
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE : // minCapacity最大能取到Integer.MAX_VALUE
MAX_ARRAY_SIZE;
}
5 modCount介绍
modCount是ArrayList的父类AbstractList中的一个字段,通过字段上的注释可以知道:
modCount记录了当前list容器进行“结构性”修改的次数,通俗点讲就是所有改变list容器size字段值的操作,如:add、addAll、remove、clear。每当调用add等方法时,modCount会+1。
该字段主要用在迭代器中,如通过iterator和ListIterator方法返回的迭代器类中。当返回一个迭代器类时,迭代器会记录下当前modCount的值,在通过迭代器遍历集合容器的过程中(使用迭代器的next、previous、set、remove、add方法),会一直判断modCount与一开始记录的值是否相同,若不同则抛出ConcurrentModificationException。
/**
* The number of times this list has been <i>structurally modified</i>.
* Structural modifications are those that change the size of the
* list, or otherwise perturb it in such a fashion that iterations in
* progress may yield incorrect results.
*
* <p>This field is used by the iterator and list iterator implementation
* returned by the {@code iterator} and {@code listIterator} methods.
* If the value of this field changes unexpectedly, the iterator (or list
* iterator) will throw a {@code ConcurrentModificationException} in
* response to the {@code next}, {@code remove}, {@code previous},
* {@code set} or {@code add} operations. This provides
* <i>fail-fast</i> behavior, rather than non-deterministic behavior in
* the face of concurrent modification during iteration.
*/
protected transient int modCount = 0;
6 Iterator()和ListIterator()
ArrayList中有Iterator()和ListIterator(),这两个方法都是继承自AbstractList,但进行了优化,分别调用后会返回Itr和ListItr两个对象,对集合容器的迭代可以通过这两个对象提供的方法进行。
6.1 Itr类底层介绍
该类提供了next()和remove()两个方法来对集合进行迭代。
private class Itr implements Iterator<E> {
int cursor = 0; // 下一次调用next()会返回的元素的索引
int lastRet = -1; // 最近一次调用next()返回的元素的索引
int expectedModCount = modCount; // 记录下对象创建时modCount字段的值
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification(); // 检查在迭代过程中集合是否被修改
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() { // 迭代器的删除方法
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); // 先进行修改检查
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
// 删除后将expectedModCount重置为,modCount,下一次调用
// checkForComodification()就不会抛出异常
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
// 若在迭代器创建之后对容器进行了增删操作,则抛出异常
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
6.2 ListItr
ListLtr继承了Itr,并且增加了previous、set、add等方法,功能比Itr类更强大,一般更推荐用ListLtr来对集合进行迭代。
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) { cursor = index; }
public boolean hasPrevious() { return cursor != 0; }
public E previous() { checkForComodification(); ...... }
public int nextIndex() { return cursor; }
public int previousIndex() { return cursor-1; }
public void set(E e) { checkForComodification(); ...... }
public void add(E e) { checkForComodification(); ...... }
}
6.3 checkForComodification()
当返回一个迭代器类时,迭代器会记录下当前modCount的值,在通过迭代器遍历集合容器的过程中(使用迭代器的next、previous、set、remove、add方法),会一直判断modCount与一开始记录的值是否相同,若不同则抛出ConcurrentModificationException。
之所以抛出异常,是因为调用集合的add、remove等方法后会使modCount+1。正因为考虑到这个因素,所以Ltr和ListLtr提供了对集合的操作来代替集合本身的add、remove等操作,在上面Ltr的remove方法中可以看到,删除指定位置的元素后,会使 expectedModCount = modCount;这样集合的状态就又恢复到初始时了。