五、ArrayList 底层核心原理 —— 扩容机制与性能优化
很多开发者使用 ArrayList 时,只知道 “它能自动扩容”,但不清楚扩容的具体逻辑 —— 比如 “什么时候扩容?”“扩容到多大?”“扩容过程消耗性能吗?”。理解这些底层原理,能帮助你写出更高效的代码。
5.1 扩容的触发条件
ArrayList 的扩容是在 “添加元素时” 触发的,核心入口是ensureCapacityInternal(int minCapacity)方法,minCapacity表示 “当前需要的最小容量”(即size + 1,因为要添加 1 个元素)。
扩容的核心逻辑可以概括为:
- 计算当前需要的最小容量
minCapacity(添加 1 个元素就是size + 1,添加 n 个元素就是size + n); - 比较
minCapacity和当前数组容量(elementData.length):- 如果
minCapacity <= elementData.length:容量足够,无需扩容; - 如果
minCapacity > elementData.length:容量不足,触发扩容。
- 如果
5.2 扩容的具体流程(JDK 1.8)
我们通过源码一步步拆解扩容流程,核心方法包括ensureCapacityInternal()、ensureExplicitCapacity()、grow()。
步骤 1:ensureCapacityInternal (int minCapacity)—— 计算最小容量
private void ensureCapacityInternal(int minCapacity) {
// 如果elementData是默认空数组(无参构造初始化的情况),则最小容量取DEFAULT_CAPACITY(10)和minCapacity的最大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 检查是否需要显式扩容
ensureExplicitCapacity(minCapacity);
}
比如:无参构造初始化后,第一次调用add(),minCapacity = size + 1 = 1,此时elementData是DEFAULTCAPACITY_EMPTY_ELEMENTDATA,所以minCapacity会被更新为max(10, 1) = 10。
步骤 2:ensureExplicitCapacity (int minCapacity)—— 判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++; // 修改计数器(用于迭代器的快速失败机制)
// 如果最小容量 > 当前数组容量:需要扩容
if (minCapacity - elementData.length > 0) {
grow(minCapacity); // 核心扩容方法
}
}
modCount是 “集合修改次数计数器”,每次 add/remove/clear 操作都会让modCount++,迭代器遍历前会记录expectedModCount = modCount,如果遍历过程中modCount != expectedModCount,就会抛出ConcurrentModificationException(快速失败机制)。
步骤 3:grow (int minCapacity)—— 执行扩容(核心方法)
grow()是 ArrayList 扩容的核心,负责创建新数组、复制元素、替换底层数组,源码:
private void grow(int minCapacity) {
// 获取当前数组容量(oldCapacity)
int oldCapacity = elementData.length;
// 计算新容量:oldCapacity + (oldCapacity >> 1) → 即oldCapacity * 1.5(右移1位相当于除以2)
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果新容量 < 最小容量:则新容量 = 最小容量(避免扩容后仍不够用)
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
// 如果新容量 > 最大数组容量(Integer.MAX_VALUE - 8):则新容量取Integer.MAX_VALUE(避免溢出)
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = hugeCapacity(minCapacity);
}
// 复制原数组元素到新数组(新容量为newCapacity),并替换elementData
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 处理超大容量的情况
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) { // 溢出(minCapacity为负,说明int超出最大值)
throw new OutOfMemoryError();
}
// 如果最小容量 > MAX_ARRAY_SIZE:返回Integer.MAX_VALUE,否则返回MAX_ARRAY_SIZE
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
// 数组的最大容量(Integer.MAX_VALUE - 8):因为某些JVM会在数组末尾保留一些头部信息
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
扩容流程总结:
- 新容量默认是旧容量的 1.5 倍(通过
oldCapacity + (oldCapacity >> 1)计算,右移操作比乘法更高效); - 如果 1.5 倍后的新容量仍小于 “最小需要容量”(比如一次性添加大量元素),则新容量直接等于 “最小需要容量”;
- 如果新容量超过
MAX_ARRAY_SIZE(In
-1

最低0.47元/天 解锁文章
814

被折叠的 条评论
为什么被折叠?



