ArrayList 扩容机制详解:从第 1 个到第 11 个元素

简介: ArrayList动态扩容详解:首次添加元素时扩容至10,后续按1.5倍增长。通过`grow()`方法实现,结合位运算优化性能,扩容代价较高,建议预设初始容量以提升效率,体现“空间换时间”设计思想。(238字)

ArrayList 的动态扩容是其核心特性之一,而扩容行为并非每次 add() 都触发,而是按需、批量增长。下面结合源码,逐步还原其扩容全过程。

✅ 场景一:添加第 1 个元素(首次扩容)
初始状态:elementData = [](空数组),size = 0
调用 add(e) → ensureCapacityInternal(1)
因使用无参构造,elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA 成立
minCapacity = max(10, 1) = 10
当前 elementData.length = 0,10 > 0 → 触发 grow(10)
执行 grow():
oldCapacity = 0
newCapacity = 0 + (0 >> 1) = 0
但 newCapacity (0) < minCapacity (10) → 修正为 10
最终:elementData = new Object[10]
📌 结果:首次添加即分配 10 个容量,避免后续频繁扩容。

✅ 场景二:添加第 2 ~ 第 10 个元素(无需扩容)
此时 elementData.length = 10,size 从 1 增至 9
每次 add() 计算 minCapacity = size + 1(最大为 10)
minCapacity (≤10) ≤ elementData.length (10) → 不进入 grow()
直接赋值:elementData[size++] = e
📌 结果:连续 9 次添加,零扩容,高效!

✅ 场景三:添加第 11 个元素(第二次扩容)
size = 10,调用 add() → minCapacity = 11
elementData.length = 10,11 > 10 → 触发 grow(11)
执行 grow():
oldCapacity = 10
newCapacity = 10 + (10 >> 1) = 10 + 5 = 15
15 ≥ 11 → 无需修正
最终:elementData = Arrays.copyOf(..., 15)
📌 结果:容量从 10 → 15,增长 1.5 倍

🔍 核心:grow() 方法逻辑

java
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5 倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; // 保底:至少满足需求
if (newCapacity > MAX_ARRAY_SIZE)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
扩容因子:oldCapacity >> 1 等价于 /2,所以新容量 = 1.5 × oldCapacity
位运算优化:比除法更快
安全兜底:若 1.5 倍仍不够(如批量添加大量元素),直接用 minCapacity

💡 性能建议

元素数量 默认扩容次数 推荐做法


100 ~4 次(10→15→22→33→49→73→109) new ArrayList<>(100)
1000 ~8 次 new ArrayList<>(1000)

预设初始容量可完全避免扩容开销,尤其在循环或批量处理场景中至关重要。

总结
ArrayList 首次扩容到 10;
后续每次扩容为 当前容量的 1.5 倍;
扩容本质是 创建新数组 + 复制旧数据,代价较高;
最佳实践:预估大小,显式指定初始容量。

理解这一机制,不仅能写出高性能代码,更能深入体会 Java 集合设计中的“空间换时间”智慧。

相关文章
|
XML Java 数据格式
如果Spring中有两个ID相同的Bean,会报错吗?
有位粉丝被 问到这样一个问题,说在Spring中,如果有两个ID相同的Bean,会不会报错?如果报错,会在哪个阶段报错? 这个问题也要分析具体的情况,才能完整的回答。我从三个方面来回答你的问题吧。
784 0
|
5月前
|
网络协议 Dubbo Java
从 TCP 到 RPC:彻底搞懂「HTTP 与 RPC用法区别」
本文深入剖析HTTP与RPC的本质区别,从TCP底层原理讲起,解析粘包拆包、协议封装等核心问题,梳理二者演进脉络。通过对比服务发现、传输性能、适用场景等维度,结合Dubbo、gRPC等框架,帮你按场景精准选型,彻底搞懂微服务通信的技术逻辑。
838 160
|
7月前
|
Rust Java Go
Go、Rust、Kotlin、Python 与 Java 从性能到生态,全面解读五大主流编程语言
本文系统对比Go、Rust、Kotlin、Python与Java五大主流语言,从性能、并发、类型系统到生态、学习曲线等维度深入分析,结合代码示例与应用场景,助你精准选型,把握技术趋势。
1793 7
|
开发框架 Ubuntu 应用服务中间件
FastCGI与spawn-fcgi安装与配置
FastCGI与spawn-fcgi安装与配置
1457 0
FastCGI与spawn-fcgi安装与配置
|
4月前
|
机器学习/深度学习 人工智能 监控
PPO算法深度解析:为什么它如此强大又如此“挑食”?
AI博主maoku深度解析PPO算法:揭秘其“在线策略”本质——为何不能重用数据、为何必须用向量化环境。从On-policy/Off-policy哲学对比,到裁剪机制原理、向量化加速实践,再到完整代码实现与调参指南,助你真正掌握工业界首选强化学习算法。
1048 12
|
11月前
|
SQL 缓存 安全
深度理解 Java 内存模型:从并发基石到实践应用
本文深入解析 Java 内存模型(JMM),涵盖其在并发编程中的核心作用与实践应用。内容包括 JMM 解决的可见性、原子性和有序性问题,线程与内存的交互机制,volatile、synchronized 和 happens-before 等关键机制的使用,以及在单例模式、线程通信等场景中的实战案例。同时,还介绍了常见并发 Bug 的排查与解决方案,帮助开发者写出高效、线程安全的 Java 程序。
557 0
|
Java
Java 中 sleep 和 wait 之间的区别?
【8月更文挑战第21天】
1967 0
|
数据采集 供应链 API
Python爬虫与1688图片搜索API接口:深度解析与显著收益
在电子商务领域,数据是驱动业务决策的核心。阿里巴巴旗下的1688平台作为全球领先的B2B市场,提供了丰富的API接口,特别是图片搜索API(`item_search_img`),允许开发者通过上传图片搜索相似商品。本文介绍如何结合Python爬虫技术高效利用该接口,提升搜索效率和用户体验,助力企业实现自动化商品搜索、库存管理优化、竞品监控与定价策略调整等,显著提高运营效率和市场竞争力。
966 3
|
存储 前端开发 Java
Spring MVC中@SessionAttributes注解使用详解
Spring MVC中@SessionAttributes注解使用详解
522 0
|
存储 搜索推荐 数据库
软件系统【标签tag功能】的两种数据库设计
软件系统中的标签功能可采用两种数据库设计。方案一,文章和Tag各一表,Tag信息存储在文章表内(`tags`和`tagids`字段),优点是模型简单,但查询效率低且易引发数据冗余和一致性问题。方案二,增加Tagmap表,用于存储标签-文章映射,利于索引查询和数据更新,适用于高效率需求,但结构更复杂。
1226 0
软件系统【标签tag功能】的两种数据库设计

热门文章

最新文章