ArrayList是可以动态增长和缩减的索引序列,它是基于数组实现的List类。该类封装了一个动态再分配的Object[]数组,当向ArrayList中添加元素时,该属性值会自动增加。如果想ArrayList中添加大量元素,可使用ensureCapacity方法一次性增加capacity,可以减少增加重分配的次数提高性能。
1、继承结构
ArrayList通过继承AbstractList实现接口中一些通用的方法,而具体的类,然后自己在实现一些自己特有的方法,这样一来,让代码更简洁。
ArrayList还实现了一个RandomAccess接口,这个接口本社没有定义任何方法只是作为一个标记,List实现使用的标记接口,表示它们支持快速(通常是恒定时间)随机访问,具体原理下面会分析。
2、源码分析
2.1、类中定义的属性
// 缺省容量,若构造方法没有指定初始容量,当添加元素的时候默认为数组分配10的长度
private static final int DEFAULT_CAPACITY = 10;
// 空对象数组,所有容量为0的对象elementData属性共用此值节约内存
private static final Object[] EMPTY_ELEMENTDATA = {};
// 缺省空对象数组,当构造方法未指定初始容量时elementData赋此值
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 元素数组
transient Object[] elementData;
// 元素个数大小,小于等于elementData.length
private int size;
// 最大数组容量,ArrayList容量时有限的
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
2.2、AbstractList的实现
AbstractList是实现了Iterable、Collection和List接口的抽象类,其中最主要的就是实现了返回迭代器的方法。
2.2.1、iterator()方法
iterator()方法是Iterable接口定义的方法,下面看一下它的实现。
public Iterator<E> iterator() {
return new Itr();
}
Itr是AbstractList的私有内部类。
private class Itr implements Iterator<E> {
// 后续调用next返回的元素索引。
int cursor = 0;
// 最近一次调用返回到下一个或上一个的元素索引。 如果通过删除调用删除此元素,则重置为-1。
int lastRet = -1;
// 迭代器认为后备列表应具有的modCount值。 如果违反了此期望,则迭代器已检测到并发
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
// 每次进行迭代之前都要检测一下迭代器创建时的版本和List最新版本是否一致,不一致抛出异常
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();
// 每次删除元素之前都要检测一下迭代器创建时的版本和List最新版本是否一致,不一致抛出异常
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
// 删除元素后更新expectedModCount
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
// 检测一下迭代器创建时的版本和List最新版本是否一致,不一致抛出异常,目的是为了防止并发操作List
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
Itr有一个成员变量expectedModCount ,初始值被赋值为modCount,modCount是AbstractList内定义的protected变量,此变量会在该类的对象结构改变的时候自增,如在添加或删除元素的时候如下(ArrayList的添加操作),如果在使用iterator方法返回Itr类型的迭代器进行元素迭代期间如果有对该迭代器所属的List进行添加或删除操作就会更改modCount(版本号)值,待到下次迭代操作调用next方法时检测迭代器版本号与List最新版本号不相同就会抛出ConcurrentModificationException异常。
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
以上就是不能再迭代元素的时候对List进行添加或删除的原因,但是如果需要在迭代的时候删除元素可是使用迭代器的remove方法,因为此方法删除元素时会将modCount++,随后又将expectedModCount 更新为最新的modCount,这样在每次检测版本号的时候又是相同的。
2.2.2、listIterator方法
listIterator方法是List接口定义的方法。
public ListIterator<E> listIterator(final int index) {
// index在0到size之间
rangeCheckForAdd(index);
return new ListItr(index);
}
ListItr继承于Itr,增加了向前访问和增加元素的方法。
2.3、构造方法
ArrayList有三个构造方法:
2.3.1、无参构造方法
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
将elementData 赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空数组,但是在首次添加元素的时候会将elementData 初始化为一个大小的10的数组,这个下面将。
2.3.2、带有int参数的构造方法
/**
* 构造具有指定初始容量的链表
*/
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);
}
}
当initialCapacity==0的时候使用上面类变量EMPTY_ELEMENTDATA代表的空数组,当initialCapacity>0的时候将elementData 初始化为一个数组长度为initialCapacity的Object数组。
2.3.3、带有Collection参数的构造方法
/**
* 按照集合的迭代器返回的顺序构造一个包含指定集合元素的列表。
*/
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// defend against c.toArray (incorrectly) not returning Object[]
// (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
此方法使用一个已有的List复制elementData中的元素到一个新的ArrayList对象,并计算好size。
2.4、add()方法
2.4.1、将指定的元素追加到此列表的末尾
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
modCount++是为了防止迭代的时候修改List,之后调用add的重载方法。
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
该方法是向elementData指定位置s添加元素e,在添加时先判断如果elementData数组满了,则先调用grow()方法扩容elementData。
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
grow方法内调用newCapacity方法新数组的大小,然后拷贝elementData 元素到新数组在赋值给elementData完成扩容。
newCapacity方法返回至少与给定最小容量一样大的容量。 如果满足,则返回当前容量增加50%。 除非给定的最小容量大于MAX_ARRAY_SIZE,否则不会返回大于MAX_ARRAY_SIZE的容量。
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
2.4.2、将指定的元素追加到指定的位置
public void add(int index, E element) {
// 检查index不能超范围
rangeCheckForAdd(index);
modCount++;
final int s;
Object[] elementData;
// 空间不足扩容
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
// 插入到指定位置
elementData[index] = element;
size = s + 1;
}
2.4.3、将指定的元素集合追加到此列表的末尾
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
modCount++;
int numNew = a.length;
if (numNew == 0)
return false;
Object[] elementData;
final int s;
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
System.arraycopy(a, 0, elementData, s, numNew);
size = s + numNew;
return true;
}
2.5、remove方法
2.5.1、删除指定位置上的元素
public E remove(int index) {
Objects.checkIndex(index, size);
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
删除元素的逻辑在fastRemove方法中。
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
可以看到删除元素不会重新分配elementData,elementData只能变大不能变小。
2.5.2、删除指定元素
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
found: {
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
break found;
} else {
for (; i < size; i++)
if (o.equals(es[i]))
break found;
}
return false;
}
fastRemove(es, i);
return true;
}
遍历elementData数组通过equals比较找到元素位置,调用fastRemove删除元素。
2.6、clear方法
public void clear() {
modCount++;
final Object[] es = elementData;
for (int to = size, i = size = 0; i < to; i++)
es[i] = null;
}
将elementData中每个元素都赋值为null,等待垃圾回收将这个给回收掉。
2.7、set方法
用指定的元素替换此列表中指定位置的元素,可以看到此方法并不会修改modCount。
public E set(int index, E element) {
Objects.checkIndex(index, size);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
2.8、indexOf()方法
indexOf()方法并没有使用父类已经实现的,父类AbstractList实现的逻辑是通过迭代的方式,这样会很慢而ArrayList是基于数组的实现,按索引访问很快。
3、RandomAccess接口有何作用
ArrayList实现RandomAccess接口,但是RandomAccess接口里面是空的!LinkedList并没有实现RandomAccess接口。
只要List集合实现这个接口,就能支持快速随机访问,然而又有人问,快速随机访问是什么东西?有什么作用?
通过查看Collections类中的binarySearch()方法,源码如下:
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
可以看到如果List实现了RandomAccess 接口,则是使用二分查找,因此这个方法传入的list需要是一个升序排列的。