1.ArrayList继承关系如下:
RandomAccess接口:是一个标志接口,表明实现了这个接口的List集合支持快速随机访问。如果是实现了这个接口的List集合,使用for循环方式获取数据比使用迭代器方式更快。
2.主要的成员变量
//实际元素个数
private int size;
//一个Object对象数组
transient Object[] elementData;
//数组的默认初始化容量10,无参构造函数的初始化容量
private static final int DEFAULT_CAPACITY = 10;
//两个空的Object数组
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
这里使用两个空的数组是用来区分ArrayList是由有参构造函数构造的还是无参构造函数构造的。以便在第一次向集合add元素的时候,知道对应的扩容方案。
//一个记录对集合的操作次数的整型变量
protected transient int modCount = 0;
3.主要方法解析
(1).add方法
在调用add方法向集合中添加元素时,会先调用ensureCapacityInternal()方法保证数组的容量,参数minCapacity所需要的的最小容量为当前元素的实际个数size + 1,让我们看看ensureCapacityInternal()这个方法。
先调用calculateCapacity()这个静态方法确认所需要的最小容量minCapacity。
这里之所以要确认一下最小容量,主要是当使用无参构造函数来创建集合时,数组的默认容量为10,第一次向集合中添加元素时会直接把数组的容量扩为10,这里的minCapacity赋值为10,有参构造函数第一次添加元素,minCapacity仍然为size + 1。
然后调用ensureExplicitCapacity()方法。
判断所需要的最小容量是否大于当前数组的长度,如果大于就调用grow()方法进行扩容。
旧容量oldCapacity为当前数组的长度,新容量
** newCapacity = oldCapacity + oldCapacity/2**
即数组容量扩容为原来的1.5倍(向下取整)。在newCapacity和minCapacity中取最大值作为数组的最终扩容容量赋值给newCapacity。然后判断最终扩容容量是否大于集合所定义的最大容量,如果大于调用静态方法hugeCapacity()。
判断之前所需的最小容量minCapacity是否大于集合定义的最大容量(整型最大值 - 8),如果大于newCapacity赋值为整型最大值,反之为集合定义最大容量。
然后调用Arrays.copyOf()进行扩容操作。
这个方法的底层是调用的一个native方法System.arraycopy()最终完成的数组拷贝操作。
4.关于循环地向list中remove元素
(1).普通for循环
使用普通for循环循环地remove元素时,要注意集合的实际容量一直在变化,你的索引也一直在变化,在遍历的时候会漏掉一些元素。
(2).增强for循环
本质上就是通过迭代器实现的,与下面的代码等效。
在增强for循环中循环地进行remove(或者add)操作,会抛ConcurrentModificationException异常,因为remove操作和add操作均会修改modCount的值,在迭代器的next方法中,每次都会调用checkForComodification()方法来校验当前modCount值与期望值是否相同,因此会抛异常。
(3).迭代器
使用迭代器可以做正常循环并删除,但是必须要使用迭代器的remove方法,不然还是会抛ConcurrentModificationException异常。