主题切换
实现原理
一、ArrayList底层核心实现
ArrayList 底层基于动态数组实现,核心数据结构为 Object[] elementData,通过数组的连续内存存储元素,并支持自动扩容机制。
- 初始状态:无参构造创建时,
elementData指向共享空数组,初始容量为 0 - 延迟初始化:第一次调用
add()方法时,才会将数组扩容到默认容量 10 - 扩容规则:每次扩容为原容量的 1.5 倍,通过
Arrays.copyOf()复制旧数据到新数组
二、ArrayList添加数据的完整流程
- 容量校验:调用
ensureCapacityInternal(size + 1),确保数组容量足够容纳新元素 - 容量计算:通过
calculateCapacity()方法,判断是否为第一次添加元素(无参构造场景),并计算所需最小容量 - 扩容触发:若所需容量超过当前数组长度,调用
grow()方法执行扩容 - 数据存储:将新元素存入
elementData[size]位置,size自增 1 - 返回结果:返回
true表示添加成功
三、扩容机制详解
1. 扩容触发条件
当 size + 1 > elementData.length 时,触发扩容操作,确保后续添加元素时有足够的空间。
2. 扩容核心逻辑
java
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
// 新容量 = 原容量 + 原容量/2(即原容量的1.5倍)
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 复制旧数组数据到新数组
elementData = Arrays.copyOf(elementData, newCapacity);
}3. 扩容过程
- 创建一个新的数组,长度为原数组的1.5倍
- 通过
Arrays.copyOf()将原数组中的元素复制到新数组中 - 将
elementData指向新数组,完成扩容
四、面试高频问题解析
问:ArrayList底层的实现原理是什么?答: ArrayList底层基于动态数组实现,使用
elementData数组存储元素。无参构造初始容量为0,第一次添加元素时扩容到10;后续扩容为原容量的1.5倍,每次扩容通过Arrays.copyOf()复制数据。添加元素时先校验容量,不足则扩容,再将元素存入数组并更新size。问:
ArrayList list = new ArrayList(10)中的 list 扩容几次?答: 该语句仅初始化了一个容量为10的数组,未发生任何扩容。因为构造函数直接创建了长度为10的数组,只有当元素数量超过10时(如添加第11个元素),才会触发第一次扩容,新容量变为15。问:ArrayList的扩容为什么是1.5倍?答: 选择1.5倍扩容是时间与空间的折中:
- 若扩容倍数过小(如1.1倍),会导致频繁扩容,数组拷贝开销大
- 若扩容倍数过大(如2倍),会造成较多的空间浪费
- 1.5倍扩容既能减少扩容次数,又能避免过度浪费空间,同时配合右移运算(
oldCapacity >> 1)实现高效计算
问:ArrayList的初始容量为什么不直接设为10?答: 为了优化内存占用,采用了延迟初始化策略:
- 无参构造创建时,不直接创建长度为10的数组,而是使用共享空数组
- 仅当第一次添加元素时,才会初始化容量为10
- 避免了创建空集合时就占用不必要的内存空间