本文主要对ArrayList源码做一次解析,以及个人的理解做一次记录
ArrayList的实现
通过对源码的阅读可以发现,ArrayList内部的所有操作,都是对内部初始化的 Object [] 的操作。这意味着对内部数据的查询相对较快速,因为数组有角标可以快速访问,相对的对数据进行增、删效率就没那么高了(数组定长,对数据的修改都是一次copy)
里面的代码也相对比较简单,挑如下几个方面说一下主要的实现
ArrayList数据操作主要方法
- get(int index);//获取指定位置的数据
- set(int index, E element);//将数据设置到指定位置
- add(E e);//将数据添加到最后
- add(int index, E element);//将数据添加到指定位置
- remove(int index);//移除指定位置的数据
- remove(Object o);//移除指定的数据
- clear();//清除所有数据
- addAll(Collection<? extends E> c);//将整个集合添加进去
- addAll(int index, Collection<? extends E> c);//从指定位置开始添加整个集合
- removeRange(int fromIndex, int toIndex);//删除指定范围的数据
- retainAll(Collection<?> c);//保留两个list的交集
ArrayList对内部数据的操作主要有如上10个方法。在操作几个添加数据相关方法时会对当前的数组进行容量判断,如果不够则进行扩容,默认是增加当前容量的一半。
对数据的增删主要通过System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length);实现。
System.arraycopy这个方法表示将src这个数组的数据从srcPos开始一共length长度的数据,复制到dest这个数组的destPos位之后。
ArrayList的扩容
先看如下这几个方法,实现的ArrayList扩容
/**
* Increases the capacity of this <tt>ArrayList</tt> instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != EMPTY_ELEMENTDATA)
// any size if real element table
? 0
// larger than default for empty table. It's already supposed to be
// at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = 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);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
每次增删数据前都会判断当前数组的容量,判断的方法主要是ensureCapacity()和ensureCapacityInternal(),一个公开一个内部调用,调用之后如果数据容量不够都会调用grow()做扩容处理。
来看一下grow()代码实现,添加了注释。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//如果容量不够先扩容一半
if (newCapacity - minCapacity < 0)
newCapacity = 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);//数据重新拷贝赋值
}
方法分析
看一下如下几个主要的方法
public void add(int index, E element) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);//这里相当于将数据做了一次移动,将index之后的数据统一往后移动了1位
elementData[index] = element;//同时将index的数据进行了重新复制
size++;
}
public E remove(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
modCount++;
E oldValue = (E) elementData[index];
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);//同add方法,这里将index之后的数据统一往前移动了1位
elementData[--size] = null; //同时这里是通过复制实现的移动最后一位数据现在是多余的,清除
return oldValue;
}
public boolean remove(Object o) {
//这里对数据做了是否为null的区分,节省了equals的比较
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
//这一步的操作是判断是否是最后一位,最后一位不需要数据拷贝移位
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; //最后一位数据肯定多余
}
public boolean addAll(int index, Collection<? extends E> c) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
//这里确定是否需要将原数组的数据往后移位
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
//将新数据拷贝进对应的位置
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
protected void removeRange(int fromIndex, int toIndex) {
// Android-changed : Throw an IOOBE if toIndex < fromIndex as documented.
// All the other cases (negative indices, or indices greater than the size
// will be thrown by System#arrayCopy.
if (toIndex < fromIndex) {
throw new IndexOutOfBoundsException("toIndex < fromIndex");
}
modCount++;
//确定需要移位的数据
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
//这里通过一个boolean值来区分,保留下的数据到底是不是交集
if (c.contains(elementData[r]) == complement)
//这里很巧妙了保存了需要的数据
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
//这里的if判断是防止上面try里面报异常,将已经修改的数据继续做保存操作
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
//清除多余的数据
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
总结
- 初始容量为10,每次扩容为当前容量的一半
- 内部维护一个size字段用于表示当前数据大小
- 主要通过System.arrayCopy()对数据做复制、移位等处理
- 由于内部使用数组实现,查询时可以通过脚标查询效率高,跟新数据则效率低