引言
ArrayList在平常工作中是用的最多的容器之一,本文将从JDK8源码(相比JDK7有一点改变)对ArrayList进行分析
1、ArrayList结构图
从下面的图中,我们可以看到ArrayList继承自AbstractList,实现了 List 、RandomAccess、Cloneable、Serializable接口,所以ArrayList 是支持快速访问、复制、序列化的。
2.分析源码
分析的顺序是从对象的属性,构造方法,常用方法进行分析
2.1、属性
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
//默认容量,即为初始值大小
private static final int DEFAULT_CAPACITY = 10;
//共享的空数组,用于初始化空实例
private static final Object[] EMPTY_ELEMENTDATA = {};
//共享的空数组,用于初始化空实例(这个属性是JDK8新增的)
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//ArrayList内部结构,是一个Object[]类型的数组,用transient修饰
private transient Object[] elementData;
//数组长度大小
private int size;
//...省略部分代码
}
2.2、构造方法
//默认的构造方法,它将创建一个空数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
//表示接受指定地容量值,初始化创建数组,建议在可估算数组大小时,创建ArrayList可指定
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);
}
}
//接收一个Collection的实体,将该Collection实体转换为ArrayList对象
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;
}
}
2.3、add方法
public boolean add(E e) {
//扩容方法
ensureCapacityInternal(size + 1);
//将添加的元素放到size++位置上,注意该步骤是非线程安全的
elementData[size++] = e;
return true;
}
2.3.1、扩容
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//判断初始化是否使用默认构造方法(EMPTY_ELEMENTDATA是初始化容量为0,DEFAULTCAPACITY_EMPTY_ELEMENTDATA使用默认构造方法,没有初始化容量)
//若是,则设置初始化大小为默认的值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++;
//若长度大于数组长度,则扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//标记3
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//新数组大小为1.5倍原数组长度
int newCapacity = oldCapacity + (oldCapacity >> 1);
//若值newCapacity比传入值minCapacity还要小,则使用传入minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//若newCapacity比设定的最大数组容量大,则使用最大整数值
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//复制原来的数组到新数组,并将引用地址指向新数组对象
elementData = Arrays.copyOf(elementData, newCapacity);
}
3、总结
- ArrayList是基于数组实现的,它的内存储元素的数组为 elementData;elementData的声明为:transient Object[] elementData;
- ArrayList中EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA的使用;这两个常量,使用场景不同。前者是用在用户通过ArrayList(int initialCapacity)该构造方法直接指定初始容量为0时,后者是用户直接使用无参构造创建ArrayList时。
- ArrayList的扩容计算为 newCapacity = oldCapacity + (oldCapacity » 1),大概是1.5倍,且不会无限扩容。
结束语
限于篇幅,本篇只详细分析ArrayList里面的add方法源码,对于开发人员来讲,最重要的就是记住初始化的ArrayList的时候一定要指定初始容量,否则扩容非常消耗性能,其次就是ArrayList是非线程安全的。
如果本篇的分析对大家有用,请大家点一个赞!