Java中ArrayList扩容机制与底层源码剖析
扩容机制
结论
transient Object[] elementData
ArrayList
中维护了一个Object
类型的数组,elementData
- 当创建
ArrayList
对象时, 如果使用的是无参构造器, 则初始elementData
容量为0, 第一次添加
扩容elementData
为10, 如需要再次扩容, 则扩容elementData
为1.5倍 - 如果使用的是指定大小的构造器, 则下次扩容会扩容1.5倍
源码解读
利用断点调试进入底层源码查看如何执行和扩容
无参构造器
代码如下
public class Main {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
for (int i = 1; i <= 10; i++) {
arrayList.add(i);
}
for (int i = 11; i <= 15; i++) {
arrayList.add(i);
}
arrayList.add(100);
arrayList.add(null);
}
}
初始化
可以发现一开始elementData
确实初始化成为一个空数组
扩容
进入for
循环中, 因为是对象数组, add()
方法也是添加的对象, 所以要先进行自动装箱
然后将对象加入
解读
modCount
用来记录集合被修改的次数- 在
add()
中会传入三个参数, 添加的对象, 被添加的数组, 以及大小 - 如果大小达到上限, 那么会执行grow()扩容
仔细阅读之后我们可以发现, 这里进行了一步判断
- 如果当前
elementData
为空, 那么我们会直接将其扩容为max(10, minCapacity)
, (那个宏的大小为10) - 否则, 那么会计算几个量
minCapacity - oldCapacity
这个是最小的增量
oldCapacity >> 1
这个是原来容量的一半
oldCapacity
这个是原来的容量
然后进入ArraysSupport.newLength()
方法, 获得新的容量, 然后扩容
查看ArraysSupport.newLength()
方法
解读
- 这里我们发现, 如果
minGrowth
比prefGrowth
小, 那么我们就会扩容1.5倍, 和结论吻合 - 但是如果扩容1.5倍解决不了问题, 就会扩容
原来容量+最小增量
, 就不是1.5倍了, (比如塞入一个大数组) - 如果扩容之后容量超过
SOFT_MAX_ARRAY_LENGTH
, 那么就会进入hugeLength
可以看到这是一个上限
进入hugeLength
可以发现, 这就是一个保险函数, 不让数组过大, 且会给你提醒
有参构造器
public class Main {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList(7);
}
}
可以看到如果是指定大小就会创造一个指定大小的Object
数组, 如果是负数就抛出异常, 如果是0就走无参构造那一套
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)