本文分析的源码版本是1.7
ArrayList继承了AbstractList并实现了List,RandomAccess, Cloneable, java.io.Serializable 接口,
AbstractList提供了List接口的默认实现(个别方法为抽象方法)。
List接口定义了列表必须实现的方法。
RandomAccess是一个标记接口,接口内没有定义任何内容。
实现了Cloneable接口的类,可以调用Object.clone方法返回该对象的浅拷贝。
通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。序列化接口没有方法或字段,仅用于标识可序列化的语义。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
因为没有final修饰,所以可以被继承。
属性
private static final long serialVersionUID = 8683452581122892189L;
//默认容量
private static final int DEFAULT_CAPACITY = 10;
//空数组,当用户调用无参构造函数时,elementData等于该数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//ArrayList基于该数组实现,用该数组保存数据, ArrayList 的容量就是该数组的长度,该数组等于EMPTY_ELEMENTDATA时,当第一次添加元素进入 ArrayList 中时,数组将扩容值 DEFAULT_CAPACITY(10)
private transient Object[] elementData;
// ArrayList实际存储的数据数量
private int size;
//数组缓冲区最大存储容量,一些 VM 会在一个数组中存储某些数据(为什么要减去 8 的原因);尝试分配这个最大存储容量,可能会导致 OutOfMemoryError(当该值 > VM 的限制时)
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
构造函数:
//创建一个初试容量为initialCapacity的、空的ArrayList,当初试容量值非法(小于0)时抛出IllegalArgumentException。
public ArrayList(int initialCapacity)
//创建一个空的ArrayList,此时其内数组缓冲区elementData=EMPTY_ELEMENTDATA={}, 容量为 0,当元素第一次被加入时,扩容至默认容量10
public ArrayList()
//创建一个包含collection的ArrayList。
public ArrayList(Collection<? extends E> c) {
//集合转 Object[] 数组
elementData = c.toArray();
size = elementData.length;
// c.toArray 可能不会返回 Object[],可以查看 java 官方编号为 6260652 的 bug
if(elementData.getClass() != Object[].class)
// 若 c.toArray() 返回的数组类型不是 Object[],则利用 Arrays.copyOf(); 来构造一个大小为 size 的 Object[] 数组
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
容量调整方法:
// 将数组缓冲区大小调整到实际 ArrayList 存储元素的大小,该方法由用户手动调用,以减少空间资源浪费的目的 如:当实际大小 < 数组缓冲区大小时,如调用默认构造函数后,刚添加一个元素,此时 elementData.length = 10,而 size = 1,通过这一步,可以使得空间得到有效利用,而不会出现资源浪费的情况
public void trimToSize()
{
//modCount是fail fast机制,在jdk1.7中去掉了volatile修饰,因为感觉没有必要为非线程安全集合浪费效率
modCount++;
if (size < elementData.length) {
elementData = Arrays.copyOf(elementData, size);
}
}
扩容分为两种:为了性能调优的手动扩容ensureCapacity()和add()方法中容量不够时的自动扩容方法ensureCapacityInternal()。代码如下:
public void ensureCapacity(int minCapacity) {
//其实就是判断是否需要扩容。首先判断现有的ArrayList容量是否为0,如果不是则选择最小扩充容量为0,如果是0,则选择最小扩充容量为10;同时再次判断人工填写的扩充容量是否比最小扩充容量大,是则进行扩容,不是则不扩容。
int minExpand = (elementData != EMPTY_ELEMENTDATA) ? 0:DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
//此方法在add()方法中被调用,其中minCapacity = size+1。首先确定ArrayList自动扩容的容量是多少。如果现有的容量是0,则选择默认的容量10进行扩容,如果现有的容量不是0,则选择现有容量+1的容量进行扩容。
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
手动和自动扩容最终调用的代码如下:
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//// 防止溢出代码:确保指定的最小容量 > 数组缓冲区当前的长度
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//这是系统提供的扩容容量,即现有的容量+现有容量的/2;注意在单线程或多线程下newCapacity都有可能会溢出,变为负数。
int newCapacity = oldCapacity + (oldCapacity>>1);
//以下四行代码有两个作用,一是判断系统提供的扩充容量和minCapacity进行比较,谁大而且没有超过最大内存限制,就选谁。二是对newCapacity溢出进行处理。
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
//超出ArrayList最大容量值时的处理
private static int hugeCapacity(int minCapacity)
{
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
其他方法
(1)深度复制
//深度复制:对拷贝出来的 ArrayList 对象的操作,不会影响原来的 ArrayList,返回一个克隆的 ArrayList 实例(深度复制的结果)
public Object clone() {
try {
// Object 的克隆方法:会复制本对象及其内所有基本类型成员和 String 类型成员,但不会复制对象成员、引用对象
@SuppressWarnings("unchecked")
ArrayList<E> v = (ArrayList<E>)super.clone();
// 对需要进行复制的引用变量,进行独立的拷贝:将存储的元素移入新的 ArrayList 中
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError();
}
}
转为Object数组的操作:
//返回 ArrayList 的 Object 数组,包含 ArrayList 的所有储存元素,对返回的该数组进行操作,不会影响该 ArrayList(相当于分配了一个新的数组)==>该操作是安全的,元素存储顺序与 ArrayList 中的一致
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
//返回 ArrayList 元素组成的数组,若a的长度小于集合的长度,则重新建立一个T[]类型的数组,并将集合中的元素按序复制到其中,若数组a的大小 >= ArrayList的元素个数,则将ArrayList的全部元素都拷贝到数组a中。
//注意:若 a 中本来存储有元素,则 a 会被 list 的元素覆盖,且 a[list.size] = null,而a[list.size + 1] 及其后的元素依旧是 a 的元素。
public <T> T[] toArray(T[] a) {
if (a.length < size)
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
判断是否集合中是否包含某一对象操作:
//判断该 ArrayList 是否包含指定对象(Object 类型,面对抽象编程,向上转型是安全的),
public boolean contains(Object o)
//顺序查找,返回元素的最低索引值(最首先出现的索引位置)
public int indexOf(Object o)
//反向查找(从数组末尾向开始查找),返回元素的最高索引值
public int lastIndexOf(Object o)
获取或改变集合中单个元素值的操作:
//返回在索引为 index 的元素:数组的随机访问。默认包访问权限,封装粒度很强,连数组随机取值都封装为一个方法。主要是避免每次取值都要强转===>设置值就没有封装成一个方法,因为设置值不需要强转
E elementData(int index)
//获取指定位置的元素:从 0 开始计数
public E get(int index)
//设置 index 位置元素的值,返回替换前的元素值。
public E set(int index, E element)
向集合中增加元素操作:
//添加新值到 list 末尾
public boolean add(E e)
//在指定位置增加新元素,原先在 index 位置的值往后移动一位
public void add(int index, E element)
//在集合的末尾增加集合c中的全部元素
public boolean addAll(Collection<? extends E> c)
//从集合的指定位置开始,增加c集合的全部元素,集合中指定位置之后的元素全部后移。
public boolean addAll(int index, Collection<? extends E> c)
删除集合中的元素操作:
//移除指定索引位置的元素:index 之后的所有元素依次左移一位
public E remove(int index)
//删除 ArrayList 中的一个指定元素(符合条件的索引最低).
public boolean remove(Object o)
//私有方法:快速删除第 index 个元素,该方法会跳过越界检查
private void fastRemove(int index)
//它会将数组缓冲区所以元素置为 null,清空后,我们直接打印 list,却只会看见一个 []
public void clear()
//删除集合中指定范围内的元素,范围是[fromIndex,toIndex).
protected void removeRange(int fromIndex, int toIndex)
//移除 list 中和 c 中共有的元素
public boolean removeAll(Collection<?> c)
//只保留 list 和 集合 c 中公有的元素
public boolean retainAll(Collection<?> c)
//批量删除
private boolean batchRemove(Collection<?> c, boolean complement)
手工序列化和反序列化:
elementData数组的前面有一个关键字transient,它是用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。所以不可以对elementData进行自动序列化,只能进行手工序列化。
//序列化
private void writeObject(ObjectOutputStream s) throws IOException
//反序列化
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException
迭代器:
能向前和向后进行偏离的迭代器:
//返回一个从指定位置开始的ListItr迭代器实例
public ListIterator<E> listIterator(int index)
//返回一个从0开始的ListItr迭代器实例
public ListIterator<E> listIterator()
//可进行双向移动的迭代器类,可以向集合中增加元素
//注意:获取前一个元素的索引时,是 cursor 前一个,而不是当前元素前一个的索引。若调用 next()后马上调用该方法,则返回的是next()返回元素的索引。add()和remove()不能连续调用。
private class ListItr extends Itr implements ListIterator<E> {}
只能向后遍历的迭代器:
//返回一个 Itr 迭代器实例
public Iterator<E> iterator()
//只能向后遍历的迭代器类,没有add()方法,remove()也不能连续调用
private class Itr implements Iterator<E> {}
sublist()方法:
注意:获取从 fromIndex 到 toIndex 之间的子集合(左闭右开区间)若 fromIndex == toIndex,则返回的空集合,对该子集合的操作,会影响原有集合,当调用了 subList() 后,若对原有集合进行添加或删除操作时,会抛出异常 java.util.ConcurrentModificationException 。该子集合支持所有的集合操作
//其返回一个SubList类的实例
public List<E> subList(int fromIndex, int toIndex)
private class SubList extends AbstractList<E> implements RandomAccess
注意这里有个小知识点:
对 ArrayList 进行操作(如 add()、clear())后,即使 elementData 实际上的长度 > 其内元素个数,但是再直接打印该list时,显示结果还是[x,x,x] (注:x 代表其内实际(或有效)存储的元素)
对于 elementData 中存储情况和直接打印list (即System.out.println(list))结果不一致的原因,是因为 AbstractCollection 中对 toString() 进行了定义。代码如下:
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}