ArrayList扩容机制

一、ArrayList 基础认知

ArrayList 是非线程安全 的动态数组,底层实现为Object[]数组,它的核心优势是支持随机访问、查询效率极高,同时封装了自动化的容量管理逻辑,屏蔽了数组固定长度的弊端。

重点:JDK 7 及之后,ArrayList 采用懒加载机制,初始化时不会直接分配容量,只有真正添加元素时,才会触发容量分配。

二、什么是自动扩容?

我们先明确核心定义:自动扩容是 ArrayList 的核心能力:

  1. 初始化时创建空数组,不占用实际内存空间;
  2. 执行元素添加操作时,正式分配内存容量;
  3. 当添加元素导致现有容量不足时,底层会自动创建一个更大的新数组,将原数组元素复制到新数组中,完成扩容。

简单来说:数组长度固定不可变,ArrayList 通过「创建新数组 + 复制元素」模拟了动态扩容效果

三、ArrayList 扩容全流程(核心源码逻辑)

我们以 JDK 8 为基准,拆解扩容的每一步执行逻辑,这也是面试高频考点。

1. 扩容触发时机

执行add()/addAll()方法添加元素时,会触发容量检查 :判断条件:当前元素个数 + 1 > 数组现有容量

  • 条件不成立:直接添加元素
  • 条件成立:立即触发扩容流程

2. 核心扩容步骤

(1)懒加载初始化

无参构造创建 ArrayList:new ArrayList()底层数组:private transient Object[] elementData = {};(空数组)第一次添加元素 时,直接将容量扩容为默认值 10

(2)计算新容量

ArrayList 默认扩容规则:新容量 = 旧容量 + 旧容量 >> 1扩容为原容量的 1.5 倍 >> 1是右移一位,等价于除以 2,运算效率远高于除法。

(3)严格的容量校验

扩容不是无限的,JDK 做了双层安全校验:

  1. 如果 1.5 倍后的新容量 < 最小需要容量(如批量添加大量元素),直接以「最小需要容量」作为新容量;
  2. 如果新容量超过MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8,则直接使用Integer.MAX_VALUE(2^31-1)作为最终容量。

关键疑问解答:① 为什么是Integer.MAX_VALUE - 8?部分虚拟机需要在数组对象头中存储数组长度信息,预留 8 字节空间,避免内存溢出(OOM),这是推荐安全上限 。② 为什么最终可以用Integer.MAX_VALUE-8是安全建议,不是物理上限,当容量不足时,JDK 会用 int 类型的最大值做兜底保障

(4)数组复制完成扩容

底层通过Arrays.copyOf()方法实现:

  1. 创建新容量的数组;
  2. 将原数组元素浅拷贝到新数组;
  3. elementData指向新数组,丢弃旧数组。

性能瓶颈:数组拷贝是耗时操作,频繁扩容会大幅降低 ArrayList 性能。

四、完整扩容流程总结

  1. 初始化:空数组,无容量分配;
  2. 首次添加:容量直接变为 10;
  3. 常规添加:检查容量,不足则扩容 1.5 倍;
  4. 容量校准:优先满足最小需求,再限制最大容量;
  5. 数组复制:完成扩容,替换底层数组。

五、ArrayList 扩容实战优化(开发必备)

扩容的核心损耗是数组复制 ,想要提升性能,核心思路:减少扩容次数

优化方案 1:指定初始容量(最常用)

如果提前知道元素数量,创建 ArrayList 时直接指定容量,从根源避免扩容:

复制代码
// 坏代码:未知会触发多次扩容
List<String> list = new ArrayList<>();
// 好代码:提前指定1000容量,零扩容
List<String> list = new ArrayList<>(1000);

优化方案 2:批量添加时手动扩容

添加大量元素前,手动调用ensureCapacity(int minCapacity)方法,一次性扩容到位:

复制代码
List<String> list = new ArrayList<>();
// 手动扩容,提前分配足够容量
list.ensureCapacity(1000);
// 批量添加元素,无额外扩容
for (int i = 0; i < 1000; i++) {
    list.add("测试");
}

优化方案 3:避免中间插入 / 删除

数组结构决定了中间操作会触发元素移动,结合扩容会进一步降低性能,高频增删场景建议使用 LinkedList。

相关推荐
xsyaaaan6 小时前
leetcode-hot100-双指针:283移动零-11盛最多水的容器-15三数之和-42接雨水
算法·leetcode
独泪了无痕7 小时前
使用Fetch API 探索前后端数据交互
前端·http·交互设计
css趣多多7 小时前
别名路径的知识点
前端
炽烈小老头9 小时前
【每天学习一点算法 2026/03/08】相交链表
学习·算法·链表
靓仔建9 小时前
Vue3导入组件出错does not provide an export named ‘user_setting‘ (at index.vue:180:10)
开发语言·前端·typescript
HalvmånEver9 小时前
7.高并发内存池大页内存申请释放以及使用定长内存池脱离new
java·spring boot·spring
EnoYao9 小时前
我写了一个团队体检报告 Skill,把摸鱼的同事扒出来了😅
前端·javascript
凤山老林9 小时前
SpringBoot 使用 H2 文本数据库构建轻量级应用
java·数据库·spring boot·后端
一碗白开水一9 小时前
【工具相关】OpenClaw 配置使用飞书:打造智能飞书助手全流程指南(亲测有效,放心享用)
人工智能·深度学习·算法·飞书