ArrayList
ArrayList是集合类中的一个重要类库。ArrayList利用数组实现了List接口,并且可以自动扩容。下面从源码的角度探讨ArrayList的实现。
构造函数
public ArrayList(int initialCapacity)
public ArrayList()
public ArrayList(Collection<? extends E> c)
构造函数有三个,一个一个来看源码。
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);
}
}
第一个构造函数,传入参数是一个int类型的值,代表了初始容量。源码也比较简单,主要是判断initialCapacity是否是大于0,如果大于零分配一个initialCapacity大小的数组;等于0分配一个大小为0的数组,小于零则抛出IllegalArgumentException。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
第二个构造函数,是无参数的构造函数,直接分配一个大小为0的数组。
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
第三个构造函数,传入了一个泛型Collection,其中Collection是List,Set等接口的父接口。泛型的类型可以是E的子类,即可以castTo E的类。下面看一下具体实现:第一行直接将c.toArray()转换的数组赋给ArrayList的内部数组。然后判断elementData的长度,如果不为0,即不是空数组,则进行下一步,如果是空数组,那么直接赋一个空数组给内部数组。这里如果不是空数组的话,会有下一步的类型转换的操作,上方的注释给出了解释,c.toArray() might (incorrectly) not return Object[]。即数组转换函数toArray()返回的不一定是Object[]的数组对象,故而下面再次进行确认,并且如果不是Object[]对象则做一次类型转换。
总而言之,ArrayList构造方法允许我们使用三种方式构造ArrayList。
List接口主要方法
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
E get(int index);
E set(int index, E element);
void add(int index, E element);
E remove(int index);
int size()
public int size() {
return size;
}
size通过内部的字段维护。
boolean isEmpty()
public boolean isEmpty() {
return size == 0;
}
无需多言,直接判断size大小是否为0.
boolean contains(Object o)
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
使用IndexOf方法判断是否存在。IndexOf方法实现是首先判断Obejct是否为null,如果为null则进行地址比较,如果不为null则遍历进行值比较,如果比较成功则返回index,否则返回-1。
Iterator iterator()
获取迭代器使用一个内部类实现,具体代码如下:
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
继续一个方法一个方法来看iterator实现。
hasNext()方法:判断cursor位置是否为size大小,如果为size大小,那么即将超过数组边界,则返回false。
next()方法:
首先第一句调用了checkForComodification();可以看到方法中使用了两个变量,一个是Iterator类中的expectedModCount,另一个是ArrayList类中的modCount。可以将modCount视作一个ArrayList的版本管理变量,即如果expectedModCount!=modCount则肯定有其他线程更改了ArrayList的modCount,那么直接抛出异常。注意该方式使用了fast-fail的理念,但是不能保证线程安全,因为modCount本身没有声明为volatile(可能出于执行速度的考虑),多个线程进行操作不能保证一定会fast-fail。
后面的语句较为简单,也基本上是一些校验语句,最后返回next的值。
remove()方法:
类似next,不过会将lastRet位置的元素给删除,cursor也会移动到lastRet的位置。
add(E e)方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
此处ensureCapacityInternal方法用于扩展内部数组的容量,具体调用了grow方法。
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
注意此处在第一次进行扩容时,会设置数组大小为DEFAULT大小10。
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);
}
首先计算capacity + capacity/2的大小,如果仍小于minCapacity则设置为minCapacity,然后判断newCapacity是否大于MAX_ARRAY_SIZE,如果大于进行hugeCapacity,最后进行数组拷贝转移。
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
带index的添加方法,基于数组拷贝方式移动index之后的元素,然后执行数组赋值。
remove(int index)方法
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; // clear to let GC do its work
return oldValue;
}
代码可见,进行一次数组复制过程,将index处之后的数组元素前移,然后设置–size为null,完成GC。