本文是基于JDK 1.8来查看的ArrayList的源码内容,不同版本的JDK,ArrayList部分的内容或有出入,但应该出入不大,可以相互参考借鉴。
在ArrayList中,有6个基本属性,其中,关键的属性为表示元素数量的size(int类型),以及存储元素的elementData(Object[] 类型),ArrayList有3个构造器,两个有参和一个无参构造器,其内容如下:
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);
}
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
this.elementData = EMPTY_ELEMENTDATA;
}
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
先说无参构造函数,是直接对存储元素的数组进行赋值,在源码中,查看DEFAULTCAPACITY_EMPTY_ELEMENTDATA,其为:private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 是一个缺省容量空元素数组。第一个有参构造函数,则直接说明了new 出一个相应空间大小的object数组,如果参数值为0,就将EMPTY_ELEMENTDATA赋值给elementData,其值等同于DEFAULTCAPACITY_EMPTY_ELEMENTDATA 。第二个有参构造函数,就是直接把一个泛型转成数组后赋值个elementData。
再看add方法,每增加一个元素,size值就进行+1,在增长的地方,将元素填充到数组对应的位置。在如下代码可以看到,在数据增加之前,对数组是否要进行扩容进行了一系列的判断,如果确实发现,数组当前的容量不够满足了,遍对数组进行复制、扩容。
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
还有一个add(int index, E element)方法,在指定位置插入元素,在插入之前,先调用rangeCheckForAdd方法,对位置进行校验是否合法,然后调用ensureCapacityInternal方法,判断是否需要扩容,接着,对数组进行复制,将index后面的元素向后移动一位,最后,将index位置的元素覆盖上去以及size++;
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1);
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
另外的addAll方法也很简单,先把泛型转为object[]后,然后判断是否将容器扩容,最后调用复制的方法以及size的大小增加即可。
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew);
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
get和set就更为简单了,都是一个基于ArrayList里属性elementData的操作,操作前对位置进行一些校验即可。get直接从数组中取得位置为index的值,set就是将位置为index的值用element替换,并将原来的旧值返回。
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
remove操作和add操作较为相同,都是一个对数组的复制,remove之前,先对位置信息进行校验,然后调用复制的方法,最后将size-1的位置设置为null,其作用是告诉垃圾回收机制将其空间回收。
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null;
return oldValue;
}
clear操作更为直接,将每一处位置设值为null即可。