第027篇 ArrayList 源码与扩容机制:1.5 倍增长的细节

简介: ArrayList面试高频题:JDK8中无参构造不分配数组,首次add才扩容至10;后续按1.5倍增长,不足时直取所需容量,上限为Integer.MAX_VALUE-8。扩容本质是Arrays.copyOf整段拷贝。关键要懂原理、避坑(如预估容量、非线程安全、subList陷阱)并结合工程实践。

ArrayList 源码与扩容机制,几乎是 Android 面试里被反复问到、且容易被答浅的一题。很多候选人张口就是“底层是数组,查询快插入慢”,听起来没问题,面试官接着问一句“无参构造 new 出来的那一刻,底层数组多大”,台面上的人一半会答错。这题考的从来不是背诵,而是能不能把一个被用滥的数据结构拆开讲清楚:容量怎么来、怎么长、长到什么时候会出问题。

先把结论放在前面:无参构造在 JDK 8 里并不会立刻分配数组,首次 add 之前底层是空数组,第一次 add 才真正扩容到默认容量 10;早期 JDK 版本则是构造时直接分配长度 10 的数组。后续每次容量不足,新容量取旧容量的 1.5 倍(old + old >> 1),通过 Arrays.copyOf 整体拷贝到新数组。这题要答好,得把扩容机制拆开看:它解决什么问题、不解决什么问题、和“手动指定容量”的分界在哪里。

机制拆解

讲清楚扩容发生在哪。add(E e) 内部先调用 ensureCapacityInternal(size + 1),算出最小所需容量,若超过当前 elementData.length,就走 grow 方法。grow 里 newCapacity = oldCapacity + (oldCapacity >> 1),也就是 1.5 倍;若 1.5 倍仍不够(比如一次 addAll 塞进一大段),则直接取所需容量;若超过 MAX_ARRAY_SIZE(Integer.MAX_VALUE - 8),再按上限兜底。这个细节很少有人主动提到,却是“一次 addAll 大集合会不会频繁扩容”这类追问的答案。理解 1.5 倍与“按需取所需容量”两条分支,才能在“批量写入”场景讲出层次。

这些坑的正确绕法

最常见的坑是预知数据量却仍用无参构造。例如从接口拉回一万条数据循环 add,会经历多次 1.5 倍扩容与整段拷贝,既吃 CPU 又产生大量短命大数组加重 GC。正确做法是 new ArrayList<>(expectedSize) 一次到位,或至少调用 ensureCapacity 提前撑开底层数组。

其次是以为 ArrayList 线程安全。多个线程并发 add,size 自增与数组写入不是原子的,会出现元素覆盖、size 超前、甚至数组越界。ArrayList 从设计上就不保证并发安全,多线程写要用 CopyOnWriteArrayList 或外部加锁。

还有一个更隐蔽的坑:用 subList 拿到的是原集合的视图而非副本,对子列表的增删会直接反映到原集合,且原集合结构一旦被修改(哪怕是通过子列表之外的方式),再操作子列表会抛 ConcurrentModificationException。把 subList 结果当独立列表传给别的方法,是生产事故里很常见的一种误用。

代码里见真章

看一段能直接跑的代码,把上面的机制落到具体写法上:

// ArrayList 扩容机制(JDK 8)
ArrayList<Integer> list = new ArrayList<>();        // 首次 add 前底层为空数组
list.add(1);                                         // 首次 add 扩容到 10
list.ensureCapacity(1000);                           // 预分配,避免反复拷贝
// 扩容:newCapacity = old + (old >> 1),即 1.5 倍
// grow() 内通过 Arrays.copyOf 整体拷贝到新数组
// 容量上限 Integer.MAX_VALUE - 8(MAX_ARRAY_SIZE)

这段代码值得盯三处:第一处,无参构造延迟分配,首次 add 才到 10,省了空集合的内存;第二处,ensureCapacity 预分配能省掉中途多次 1.5 倍扩容的拷贝开销;第三处,扩容本质是 Arrays.copyOf 的整段数组迁移,数据量大时这笔开销不可忽视。面试讲到这一层,基本就稳了。

这题在面试里怎么问、怎么答

“请简单介绍一下 ArrayList 源码与扩容机制,它在 Android 开发中起什么作用?”按“是什么 → 怎么扩 → 项目里怎么用”递进,别超三分钟。ArrayList 用动态数组承载元素,随机访问 O(1),尾部增删均摊 O(1),中间插入 O(n)。在 Android 里,列表渲染的数据源、接口返回的对象集合、ViewHolder 里的临时容器都大量用到它。如果要把使用约定沉淀成团队规范,建议加一条:已知批量数据规模时,构造即指定容量,别让扩容在循环里偷偷发生。

“ArrayList 的扩容底层原理是什么?能不能详细说一下?”从 add 的 ensureCapacityInternal 讲起:计算最小容量 → 超过当前长度则 grow → 新容量 1.5 倍 → Arrays.copyOf 迁移。补充两个常被忽略的点:一是 addAll 大集合时 1.5 倍不够会直接取所需容量,避免二次扩容;二是 MAX_ARRAY_SIZE 上限兜底。机制别空讲,配上核心代码最稳。

“在使用 ArrayList 时遇到过什么问题?”讲真实案例:某次首页瀑布流从接口分页拉数据,用无参构造逐页 addAll,页面越翻越卡,profiler 抓到频繁的 Arrays.copyOf 大数组分配。修复为按首屏预估总量一次性构造容量后,卡顿消失、GC 次数明显下降。量化的修复效果最加分。

“ArrayList 和相关的替代方案相比,有什么优劣?”与 LinkedList 比,读多写少、随机访问场景 ArrayList 更合适,只有频繁两端增删才轮到 LinkedList;与数组比,ArrayList 自动扩容、有丰富 API,代价是轻微装箱与扩容拷贝;与线程安全容器比,ArrayList 非并发安全,写竞争场景要换 CopyOnWriteArrayList 或加锁。选型就按读写特征来,能讲清代价才叫真懂。选型时要把“多线程并发 add 导致元素覆盖或越界”这类代价摆到台面上,再决定是否引入。

再补一个工程上的细节:trimToSize 与容量浪费的取舍。ArrayList 经历过多次扩容后,底层数组往往比实际元素多出一截(例如放了 11 个元素,底层数组却是 16)。长期持有这种“虚胖”的集合,会无谓占用内存;在集合生命周期明确结束于一次性构建的场景(比如把查库结果封进一个 DTO 后不再变更),调用 trimToSize 能把底层数组收缩到刚好容纳元素,降低内存峰值。但 trimToSize 本身是一次整体拷贝,不能在对性能敏感的循环里滥用。另一个值得提的点是 modCount 与 fail-fast:ArrayList 在迭代中若被结构性修改会抛 ConcurrentModificationException,边遍历边删除要走迭代器的 remove(),否则要么抛异常要么跳元素。这些细节单看都不复杂,串起来却是 ArrayList 在真实项目里“用得对不对”的分水岭。

给正在准备面试的你

把 ArrayList 源码与扩容机制画成一张小图:无参构造(延迟空数组)→ 首次 add 到 10 → 不足则 1.5 倍 grow → Arrays.copyOf 迁移。面试中关于 ArrayList 的问题,关键在于能够从原理、应用、踩坑三个层面给出有深度的回答。

再补工程案例与踩坑——应用落点是给一个已知十万条的列表场景显式指定初始容量,对比有无预分配下的构造耗时与 GC 次数。

复习时别孤立刷题:LinkedList 与 ArrayList 选型——两者常一起考,边界提前划清楚。

划两句重点:无参构造延迟到首次 add 才分配默认容量 10,省了空列表内存;扩容走 1.5 倍加 Arrays.copyOf,已知规模时建议显式指定容量。

下一篇聊 LinkedList 与 ArrayList 选型:随机访问与插删的权衡——沿着今天这条主线继续往前走。


如果这篇文章对你有帮助,欢迎点赞、在看、转发三连。你的支持就是这个系列持续更新的动力。

「Android软件开发面试·从入门到精通」连载系列

上一篇:集合框架总览:Collection-与-Map-两大家族

下一篇预告:LinkedList-与-ArrayList-选型:随机访问与插删的权衡

有任何问题欢迎在评论区留言交流。

相关文章
|
17天前
|
人工智能 JSON API
全网刷屏的 Jev 模型正式开放!一手实战测评 + 保姆级教程
全网爆火的 Jev 模型是什么?有什么用?怎么使用?怎么接入 AI 编程工具?效果真的好么?傻子可懂的 Jev 保姆级实战教程 + 项目实战测评来啦
8483 24
|
16天前
|
人工智能 并行计算 PyTorch
秋叶 ComfyUI 2026 整合包 v3.2 完整部署教程:Python 3.13 + Torch 2.13 全栈升级
秋叶aaaki ComfyUI 2026年8月整合包v3.2正式发布!全面升级Python 3.13.11、PyTorch 2.13.0+cu130及ComfyUI v0.30.2,原生支持MiniMax H3、Wan 2.2、Qwen-Image-2.1等2026主流音视频/图像模型,解压即用,无需环境配置。
2826 14
|
15天前
|
人工智能 测试技术 API
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
Jev是TypeSafe AI推出的“系统一模型”,不生成文本,专做毫秒级结构化决策:Choice(多选)、Score(打分)、Noul(是非概率)。响应快193倍、成本低444倍,适合工单路由、内容审核、测试定级等高频判断场景。
2021 4
最近全网爆火的 Jev 到底是什么?适合干什么、怎么用,一篇讲透!
|
14天前
|
人工智能 编解码 并行计算
MiniMax-H3 一键整合包技术文档:8G 显存运行 AI 漫剧制作 —— 角色替换 / 动作迁移 / 文图生视频部署与调参指南
MiniMax H3 是 MiniMax 开源的全模态视频生成模型,支持文/图/音/视多条件输入,输出最高2K、15秒带双声道音频视频。本文档详述其Int8量化版在8GB显存下的本地一键部署、三段式工作流(EDIT/REPLACE/CONTINUE)、参数调优及常见问题排查。(239字)
|
10天前
|
人工智能 Linux 开发者
【2026国内使用】Codex安装过程一篇讲透(Win/Mac/Linux全支持)
Codex是OpenAI推出的AI编程智能体,可读取本地项目、理解需求并自动修改代码。支持桌面GUI、命令行(CLI)及VS Code/Cursor插件三种形态,覆盖可视化操作、终端高效开发与编辑器无缝集成场景,助开发者用自然语言驱动编码全流程。(239字)
【2026国内使用】Codex安装过程一篇讲透(Win/Mac/Linux全支持)
|
4天前
|
人工智能 JSON Linux
【全网最详细】ComfyUI使用教程:下载+本地部署+配置+工作流搭建一篇搞定(2026最新版)
ComfyUI是一款免费开源的本地AI绘图工具,采用节点式工作流设计,支持文生图、图生图、局部重绘、放大、换脸等多种功能。可离线运行,依赖显卡加速,无需联网。支持自定义流程保存与分享,插件生态丰富,适合进阶用户。(239字)
|
10天前
|
人工智能 JSON 编解码
【2026最新版】ComfyUI本地部署教程,新手也能看懂!
ComfyUI是本地运行的AI绘画工具,采用节点式工作流设计:通过拖拽连接“加载模型”“提示词编码”“采样”“解码”等模块,实现高度可控的文生图。新手推荐使用秋叶整合包,一键启动、内置模型管理与插件安装器,轻松上手。(239字)

热门文章

最新文章