预备知识
1、了解方法System.arraycopy(src, srcPos, dest, destPos, length)。这是一个本地方法,方法的主要作用是将src数组中从srcPos位置开始的元素复制到目标数组dest中,并从dest的destPost位置开始填充。length为需要复制src的中元素的个数。
2、位运算“>>”的作用:xx>>1相当于xx/2
3、另外需要说明的是次分析是基于jdk1.7.0_79
ArrayList
继承关系
*需要说明的是这里只分析ArrayList,所以AbstractList的继承体系并没有详细画出
实现原理
在ArrayList的实现原理其实很简单:在内部使用一个Object数组存放数据,当需要储存数据的数据量大于这个数组的长度时,则重新创建一个数组,将原数组的数据复制到这个新数组中,这样就实现了自动扩容。
源码分析
为了在分析的时候,减少代码注释和方法跳转说明,先来看几个比较重要的成员变量和私有方法,熟悉完成下面的属性和私有方法,ArrayList的核心源码就差不多已经了解了。
成员属性
//定义在ArstractList中,每增加删除一个元素都会+1
protected transient int modCount = 0;
//默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//长度为0的数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//用于存放元素的数组
private transient Object[] elementData;
//记录元素个数
private int size;
//最大允许保存元素数量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
私有方法
//核心方法:从方法名可以看这,这个是保证elementData是否有足够容量来存放元素的方法
private void ensureCapacityInternal(int minCapacity) {
//如果elementData还是0长度数组
if (elementData == EMPTY_ELEMENTDATA) {
//DEFAULT_CAPACITY默认为10的
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
//核心方法:这里开始计算,判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 如果minCapacity>elementData.length,需要扩容了
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//核心方法:自动扩容的实现
private void grow(int minCapacity) {
//保存旧容量
int oldCapacity = elementData.length;
//计算出新容量,即在旧容量的基础上扩容50%
int newCapacity = oldCapacity + (oldCapacity >> 1);
//这里是对newCapacity 进行修正,避免newCapacity>MAX_ARRAY_SIZE或<旧容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 这里是将elementData复制到一个新数组中,新数组的长度为之前计算出来的newCapacity
elementData = Arrays.copyOf(elementData, newCapacity);
}
//这里检查index有超出元素的个数
private void rangeCheck(int index) {
//size记录了elementData中保存的元素数量,并不一定等于elementData.length。
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
//与rangeCheck相似,只是增加了判断index<0
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
现在从构造方法开始,逐步分析在日常生活中常用的方法。
public ArrayList() {
super();
//默认的构造函数是不会创建新的Object数组的
this.elementData = EMPTY_ELEMENTDATA;
}
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
//创建一个Object数组
this.elementData = new Object[initialCapacity];
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
size = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
//这个方法底层也是调用的System.arraycopy(src, srcPos, dest, destPos, length)这个方法
elementData = Arrays.copyOf(elementData, size, Object[].class);
}
//添加一个元素,核心在ensureCapacityInternal方法中
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
//添加元素到指定位置,核心点在如何空出index这个位置
public void add(int index, E element) {
rangeCheckForAdd(index);//边界检查
//自动扩容判断,保证elementData有足够的容量来存放元素
ensureCapacityInternal(size + 1);
//这里实现了将elementData中的元素,将index及其之后的元素全部向后移一位,这样就将index位置空出
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//将e插入到index位置
elementData[index] = element;
size++;
}
//添加一个集合
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
//保证element有足够的空间
ensureCapacityInternal(size + numNew);
//复制数据到elementData中
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
//将一个集合添加到指定的位置,其实现方式与add(index, e)类似,都是通过先挪出足够的空间,再将数据复制到其中
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
//保证element有足够的空间
ensureCapacityInternal(size + numNew);
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;
}
//删除指定位置的元素
public E remove(int index) {
//范围检查,超过范围会抛出一异常
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
//重新整理elementData,即将index后面的元素全部往前移一个位置,与add刚好相反
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;
}
//删除指定元素
public boolean remove(Object o) {
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;
}
//删除指定元素的最终实现,其实与remove实现几乎一样,少了一个范围检查而已
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; // clear to let GC do its work
}
//删除指定范围内的元素,实现方式就是拿toIndex之后的元素,覆盖掉fromIndex之后的元素
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
//将toIndex之后的元素前移toIndex-fromIndex位
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// 计算出新的元素个数,将清除超过newSize的元素,因为它们是已经删除的
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
//更新size值
size = newSize;
}
//查找元素,没有什么可分析的,逐个比较元素而已。
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
//清楚所有元素
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
现在常用方法已经分析完成,最后在看看Itertaor是怎么实现的
private class Itr implements Iterator<E> {
int cursor; //当前访问元素的坐标
int lastRet = -1; //上一次返回元素的坐标
int expectedModCount = modCount;//亮点来了:将ArrayList被修改的次数保存下来,想要知道有什么用,继续看~_~
//为了看两点,我把这段代码位置提前了
final void checkForComodification() {
//快看、快看,亮点出来了:这里判断了“当前的modCount”与“创建Iterator对象时的modCount”是否一样,如果不一样,则会抛出ConcurrentModificationException异常。看到这里各位看官是否想到ArrayList不支持多线程修改的问题了~_~。其实抛出ConcurrentModificationException不仅仅可以发生在多个线程同时修改的时候,及时在单线程也可能发生,具体怎么发生各位肯定已经想到了。
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
//只要当前范围元素的坐标不等于size,那么就还有元素
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
//检查ArrayList有没有被修改过
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
//重复检查ArrayList有没有被修改过
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
//检查ArrayList有没有被修改过
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
//内部重新获取最新的modCount值
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
另外ArrayList内部还有一个SubList,不过了解上面实现之后,SubList也简单了,想要了解各位可以自己看看,这里就不在描述。
总结
其实ArrayList的实现相对比较简单,内部也就一个Object[]存放数据。不像HashMap那么相对复杂。只需要理解了它的自动扩容机制,ArrayList的源码就简单了。