随机访问
因为 ArrayList 是基于数组实现的,所以支持快速随机访问。RandomAccess 接口标识着该类支持快速随机访问。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
内部结构
扩容
添加元素时使用 ensureCapacityInternal() 方法来保证容量足够,如果不够时,需要使用 grow() 方法进行扩容,新容量量的大小为 oldCapacity + (oldCapacity >> 1) ,即 oldCapacity+oldCapacity/2。其中 oldCapacity >> 1 需要取整,所以新容量大约是旧容量的 1.5 倍左右。
扩容操作需要调用 Arrays.copyOf() 把原数组整个复制到新数组中,这个操作代价很高,因此最好在创建 ArrayList 对象时就指定大概的容量大小,减少扩容操作的次数。
删除元素
需要调用 System.arraycopy() 将 index+1 后面的元素都复制到 index 位置上,该操作的时间复杂度为O(N),可以看到 ArrayList 删除元素的代价是非常高的。
默认大小
首先看默认构造方法
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
我们看到默认数组直接赋值为 DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
计算容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
显然如果在初始化集合时不初始化数组容量(或者说长度),该数组的默认长度为0;
初始化长度为0的数组,怎么添加数据?下面我们讨论添加第一个元素的过程
我们看集合的add方法
在添加元素时;会先去判断集合是否需要扩容;
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 默认容量为10
private static final int DEFAULT_CAPACITY = 10;
private void ensureCapacityInternal(int minCapacity) {
// minCapacity = 1
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 这里肯定返回DEFAULT_CAPACITY = 10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
// minCapacity = 10;
modCount++;
// overflow-conscious code
// 扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
grow扩容
private void grow(int minCapacity) {
// minCapacity = 10
// overflow-conscious code
int oldCapacity = elementData.length;
// oldCapacity = 0;
// 1.5倍扩容
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);
}
第一次add的时候ArrayList扩容到10
总结
- ArrayList底层的数据结构就是数组,数组元素类型为Object类型,即可以存放所有类型数据。我们对ArrayList类的实例的所有的操作底层都是基于数组的。
- 实现了RandomAccess接口:表明ArrayList支持快速(通常是固定时间)随机访问。在ArrayList中,我们可以通过元素的序号快速获取元素对象,这就是快速随机访问,所以在遍历它的时候推荐使用for循环。
- ArrayList区别于数组的地方在于能够自动扩展大小
- ArrayList由于本质是数组,所以它在数据的查询方面会很快,而在插入删除这些方面,性能下降很多
- ArrayList初始集合不初始化数组的容量的时候,默认值为0
- 添加元素后,扩容为10,之后每次扩容为原来的0.5倍