ArrayList作为java中常用的集合类型有哪些特点需要我们了解呢?本文将基于jdk1.8源码来一步步列出ArrayList有哪些需要令人注意的要点。
1 RandomAccess随机访问接口
ArrayList继承实现关系如下图:
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
从该图我们可以看出,ArrayList实现了RandmoAccess接口,RandmoAcess接口实现如下:
public interface RandomAccess {
}
RandmoAccess接口中实际上并无任何实现,该接口只是表示实现了该接口的类能够提供快速访问功能。
2 底层是数组类型
ArrayList实现了RandmoAccess接口代表能够进行快速访问,而ArrayList的快速访问功能实际上是靠数组实现的,下面是ArrayList中最重要的两个属性:
//ArrayList实际存数据的地方
transient Object[] elementData;
//ArrayList的大小
private int size;
下面我们来看ArrayList的默认构造方法:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
默认构造方法会将elementData属性赋为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,其中DEFAULTCAPACITY_EMPTY_ELEMENTDATA属性是一个空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
接下来我们看ArrayList中的add和get方法:
public boolean add(E e) {
//确认是否扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public E get(int index) {
//检查是否越界
rangeCheck(index);
return elementData(index);
}
可以看出Arraylist底层是通过数组来操作的。
3 惰性初始化
ArrayList使用默认构造器创建类时创建的是一个空的数组,那么ArrayList是如何能够使用add方法存数据的呢?我们可以详细看看add方法中的ensureCapacityInternal方法源码:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//DEFAULT_CAPACITY常量数值是10
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
//记录修改次数
modCount++;
if (minCapacity - elementData.length > 0)
//扩容方法
grow(minCapacity);
}
从以上源码可以看出当ArrayList为一个空数组时,会赋予一个默认的扩容大小10,然后再进扩容方法里扩容ArrayList,因此ArrayList使用默认构造器初始化时,不会立马初始化数组大小,而是等待调用add方法后才会进行初始化,且初始化大小为10。
4 位运算1.5倍扩容
我们可以查看ArrayList的grow方法来看看arrayList实际是如何扩容的:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//位运算1.5倍扩容
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
//判断minCapacity是否超出最大整数值
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;
}
以上代码分为如下几个步骤:
- 首先用位运算对原数组进行1.5倍扩容得到新的扩容值newCapacity
- 再用新的扩容值newCapacity与实际传来的所需扩容值minCapacity进行比较,若newCapacity比minCapacity大则用newCapacity,反之则用minCapacity并判断minCapacity是否合法,这样得出的值为一个最终扩容值
- 最后用Arrays.copyOf方法对最终选出的扩容值进行扩容