前言
我们都知道ArrayList集合只能存储引用类型数据(Long、Short、Integer、Double、Float是引用类型),数组两者都可以存储(基本数据类型:long、short、int、double、float);ArrayList长度可变,而数组长度不可变。
但实际上集合ArrayList底层也是由数组实现的,这里我们来开始研究底层实现原理。
一.底层分析
/**
* 用于默认大小的空实例的共享空数组实例。
* 我们将其与EMPTY_ELEMENTDATA区分开来,以了解何时膨胀多少添加第一个元素。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 存储ArrayList元素的数组缓冲区。
* ArrayList的容量就是这个数组缓冲区的长度。
* 任何带有elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的空ArrayList
* 将在添加第一个元素时扩展为DEFAULT_CAPACITY。
*/
// Android-note: Also accessed from java.util.Collections
transient Object[] elementData; // non-private to simplify nested class access
/**
* 数组列表的大小(包含的元素数量)。
*
* @serial
*/
private int size;
/**
* 构造一个初始容量为10的空列表。
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
先看构造方法,从ArrayList空参数构造方法中可以看出ArrayList创建的时候整了一个空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA),也就是ArrayList创建时,数组初始长度为0。
/**
* 将指定的元素追加到此列表的末尾。
*
* @param e 元素添加到此列表
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
add(E e)方法中调用(elementData[size++] = e)添加元素前,需要先调用ensureCapacityInternal(size + 1),进行扩容校验。
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 数组初始容量是这里确定的
// 如果调用add添加单个元素,数组初始容量取DEFAULT_CAPACITY=10。
// 如果调用addAll添加一个集合元素,比较集合长度和DEFAULT_CAPACITY,数组初始容量取最大值
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
ensureCapacityInternal方法中
elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA如果为true,初始数组是空,表示第一次添加元素。
数组初始容量是这里确定的,如果调用add添加单个元素,minCapacity必然小于DEFAULT_CAPACITY,数组初始容量取DEFAULT_CAPACITY=10;
如果调用addAll添加一个集合元素,比较集合长度和DEFAULT_CAPACITY,数组初始容量取其中最大值。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
// 数组中已有元素数量+1是否大于当前数组长度
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* 要分配的数组的最大大小。
* 有些虚拟机在数组中保留一些头字。
* 尝试分配更大的数组可能会导致
* OutOfMemoryError:请求的数组大小超过虚拟机限制
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* 增加容量,以确保它至少可以容纳由最小容量参数指定的元素数量。
*
* @param minCapacity 所需的最小容量
*/
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);
}
ensureExplicitCapacity(int minCapacity) 方法主要校验是否扩容
每次添加元素前,都会判断if (minCapacity - elementData.length > 0),
检测数组中已有元素size+新增元素数量是否大于当前数组长度,大于表示数组元素已满,需要进行扩容。
(注:通过add方法添加元素,新增元素数量 则为1;使用addAll(Collection<? extends E> c)新增元素,新增元素数量 为集合c的长度)
grow(int minCapacity)方法开始进行具体扩容操作:
-
第一次添加 (调用add(E e)) 元素
数组初始长度elementData.length为0,minCapacity为10,newCapacity -
minCapacity < 0为true,newCapacity 赋值为minCapacity,得到新数组容量newCapacity
为10, 最后通过**Arrays.copyOf(elementData, newCapacity)**创建新数组并原数组的元素拷贝到新数组中。 -
后面每次再添加元素前,都会检测数组中已有元素数量+1是否大于当前数组长度(minCapacity -elememntData.length>0 等于size+1-elememntData.length>0);
如果是,计算新数组容量为原数组容量的1.5倍:
int newCapacity =oldCapacity + (oldCapacity >> 1)
创建一个容量为原数组容量的1.5倍的新数组,并将将原数组的元素拷贝到新数组中:
Arrays.copyOf(elementData,newCapacity
新数组容量具体计算
int newCapacity = oldCapacity + (oldCapacity >> 1)
其中oldCapacity >> 1,原数组容量oldCapacity=10的时候,位移运算如下图
oldCapacity=10向右位移1得出结果为5,oldCapacity + oldCapacity>>5 = 15,也就是1.5倍。
二.总结:
ArrayList底层是由数组实现的,第一次添加元素前,会创建一个初始容量为 10的数组。
后面每次再添加元素前,都会检测数组中已有元素数量+新增元素数量是否大于当前数组长度,如果是,创建一个容量为原数组容量的1.5倍的新数组,将原数组的元素拷贝到新数组中。