我们知道常用的List集合有ArrayList、LinkedList、Vector,它们都实现了List接口,下面我们分别以构造方法、增删改查等角度分析它们的底层实现,让我们开始吧……
transient Object[] elementData;
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
从无参构造方法中可以看出,ArrayList底层就是使用数组进行数据存储的,默认初始化一个容量为0的数组。
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,则实例化一个initialCapacity大小的数组,如果=0,则实例化一个空数组。
// 默认容量为10
private static final int DEFAULT_CAPACITY = 10;
// 添加元素,赋值给数组的下一个位置
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 确定数组的内部容量
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 计算容量,如果添加首个元素,那么minCapacity为默认容量大小10
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// 如果当前数组的容量不满足当前数组所需要的最小容量,进行扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
// 数组进行1.5倍扩容
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);
}
// 将数组的容量扩容到Integer.MAX_VALUE
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
ArrayList添加元素,本质上就是数组添加元素,因为数组添加元素需要初始化大小,所以数组这里给了默认的大小10,每次新增元素之前,都要对数组的大小进行确认,如果数组的大小不满足添加元素所需要的数组最小容量时,则需要对数组进行扩容,扩容的大小,这里运用的右移位运算,右移1位等于除以2的1次幂,所以这里每次是1.5倍扩容。
至于后面为什么还要加上newCapacity < minCapacity的判断呢,那是因为假如oldCapacity无限接近Integer.MAX_VALUE时,再进行1.5倍的扩容会得到负数,(至于为什么会等于负数,因为Integer在JVM中占用4个字节,每个字节8bit,也就是32位,具体大家可以看一下深入浅出之原码、反码、补码)
假如数组扩容后newCapacity大于MAX_ARRAY_SIZE,那么直接将数组的大小设置为Integer.MAX_VALUE。
ArrayList中期望的数组最大容量是Integer.MAX_VALUE - 8,JDK的注释中给了解释:Some VMs reserve some header words in an array. Attempts to allocate larger arrays may result in OutOfMemoryError: Requested array size exceeds VM limit(一些虚拟机在数组中保留了一些头部,尝试分配更大的数组可能会导致OutOfMemoryError:请求的数组大小超过VM限制)。
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
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;
}
如果删除的元素为null,则删除集合中的所有null值,否则调用遍历出被删除元素所在的位置,调用fastRemove(index)
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
}
代码很简单,计算数组中被删除元素右侧需要移动的位数,调用System.arraycopy将删除元素后面的值整体向前复制挪一位,再将数组最后一位的值置为null,达到数组删除元素后面的值整体前移的效果。
数据结构和ArrayList一样,都是数组,实现方法也类似,只不过在方法头上加了synchronized关键字,所以是线程安全的。