首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【集合框架ArrayList底层扩容机制】

【集合框架ArrayList底层扩容机制】

作者头像
艾伦耶格尔
发布2025-08-28 15:51:37
发布2025-08-28 15:51:37
1300
举报
文章被收录于专栏:Java基础Java基础

ArrayList 是我们最熟悉的 Java 集合之一,但你是否真正了解它在“幕后”是如何工作的?为什么说它“动态”?扩容机制到底是怎样的?

今天,我们将深入 ArrayList 的源码层面,一步步剖析它的创建、添加、扩容等核心机制。通过这篇解析,你将彻底明白 ArrayList 的“成长”奥秘。


一、ArrayList 的诞生:一个“空”数组的起点

当我们执行 new ArrayList<>() 时,ArrayList 在底层做了什么?

代码语言:javascript
复制
ArrayList<String> list = new ArrayList<>();

核心真相:此时,ArrayList 并没有立即创建一个长度为 10 的数组!相反,它非常“吝啬”,创建了一个长度为 0 的空数组

这个空数组就是 ArrayList 的核心存储结构,其名字在源码中为 elementData

代码语言:javascript
复制
// ArrayList 源码中的定义
transient Object[] elementData; // 非私有,以简化嵌套类访问

同时,ArrayList 定义了一个关键的 int 变量:size

size 变量的双重身份

size 这个变量在 ArrayList 中扮演着至关重要的双重角色:

  1. 元素的个数:它代表了当前 ArrayList 中实际存储了多少个元素。
  2. 下一个元素的索引:它指向了下一个新元素将要被插入的位置(即索引)。

举个例子:当 size = 0 时,表示集合为空,下一个元素将被添加到索引 0 的位置。


二、成长的第一步:添加第一个元素

当我们调用 list.add("Hello") 时,发生了什么?

  1. 检查容量add 方法首先会检查当前 elementData 数组的容量是否足够容纳新元素。由于 elementData 是一个长度为 0 的空数组,显然容量不足。
  2. 触发首次扩容:因为是第一次添加元素,会触发一个特殊的扩容逻辑。此时,ArrayList 会创建一个新的数组,其长度为 DEFAULT_CAPACITY(默认容量,值为 10)。
  3. 元素赋值:新元素 "Hello" 被放入新数组的索引 0 位置。
  4. size 增长size 变量从 0 增加到 1

此时,ArrayList 的内部结构如下:

  • elementData:指向一个长度为 10 的新数组。
  • size:值为 1。

三、动态扩容的核心:1.5倍的“成长”法则

随着我们不断添加元素,当 size 达到当前 elementData 数组的长度时(即数组“存满”),就迎来了常规扩容

扩容时机一:单个元素添加导致的扩容

假设我们已经添加了 10 个元素,size 变为 10,数组已满。此时再添加第 11 个元素:

  1. 检测到容量不足add 方法发现 size == elementData.length
  2. 计算新容量ArrayList 会计算新数组的长度。这个长度是原数组长度的 1.5 倍
    • 原长度:10
    • 新长度:10 + (10 >> 1) = 10 + 5 = 15
  3. 创建新数组并复制:创建一个长度为 15 的新数组,并使用 Arrays.copyOf 将原数组的 10 个元素全部复制到新数组中。
  4. 更新引用elementData 指向这个新的、更大的数组。
  5. 添加新元素:将第 11 个元素放入新数组的索引 10 位置。
  6. size 更新size 变为 11。

这个过程会不断重复。当长度为 15 的数组再次装满时,下一次扩容的新长度将是 15 + (15 >> 1) = 15 + 7 = 22

四、智能扩容:应对“批量添加”的挑战

ArrayList 的扩容机制非常聪明,它能应对一次性添加大量元素的场景。

扩容时机二:批量添加元素(addAll

假设当前 ArrayList 已经有 10 个元素(size=10),数组长度也为 10(已满)。此时,我们调用 list.addAll(anotherList),而 anotherList 包含 100 个元素。

如果按照常规的 1.5 倍扩容,新数组长度为 15,但这 15 个位置显然无法容纳接下来要添加的 100 个元素。

ArrayList 如何应对?

  1. 计算最小所需容量ArrayList 会先计算出容纳所有新元素所需的最小容量
    • 当前 size:10
    • 要添加的元素数:100
    • 最小所需容量:10 + 100 = 110
  2. 比较并决策
    • 常规扩容长度:15
    • 最小所需容量:110
  3. 选择更优方案:由于 15 < 110,常规扩容无法满足需求。此时,ArrayList 会放弃 1.5 倍的规则,直接创建一个长度为 110 的新数组
  4. 复制与添加:将原数组的 10 个元素和 anotherList 的 100 个元素全部复制到这个长度为 110 的新数组中。

核心原则ArrayList 的扩容策略是以实际需求为准。1.5 倍是常规情况下的优化增长,但当批量添加时,它会优先保证新数组的长度至少等于 size + 新增元素个数,避免了因扩容不足而导致的多次复制,极大提升了效率。


五、总结:ArrayList 的成长哲学

通过这次源码级的剖析,我们可以总结出 ArrayList 的核心工作原理:

阶段

关键动作

容量变化

创建

初始化一个长度为 0 的 elementData 数组

0

首次添加

创建一个长度为 10 的新数组

10

常规扩容

容量不足时,创建 1.5 倍长度的新数组并复制

10 → 15 → 22 → ...

批量扩容

1.5 倍不够时,以 size + 新增元素数 为准创建新数组

10 (满) + 100 → 110

ArrayList 的“动态”并非魔法,而是通过延迟初始化智能扩容策略实现的。它用 size 精确管理元素数量和插入位置,用 1.5 倍的优雅增长平衡内存与性能,并在面对批量操作时展现出“以需为本”的智慧。

理解了这些底层机制,你就能更自信地使用 ArrayList,并预判其在不同场景下的行为。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-08-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、ArrayList 的诞生:一个“空”数组的起点
    • size 变量的双重身份
  • 二、成长的第一步:添加第一个元素
  • 三、动态扩容的核心:1.5倍的“成长”法则
    • 扩容时机一:单个元素添加导致的扩容
  • 四、智能扩容:应对“批量添加”的挑战
    • 扩容时机二:批量添加元素(addAll)
  • 五、总结:ArrayList 的成长哲学
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档