随机访问
因为 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;}
// 默认容量为10private static final int DEFAULT_CAPACITY = 10;private void ensureCapacityInternal(int minCapacity) {// minCapacity = 1ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private static int calculateCapacity(Object[] elementData, int minCapacity) {// 这里肯定返回DEFAULT_CAPACITY = 10if (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 codeint 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倍
