Java中ArrayList的扩容机制
1.简介
ArrayList的底层基于数组来实现,故具备一些数组的基本特性,例如:
- 获取元素效率较高:可以直接通过索引查找
- 插入元素效率较低:节点后的数据都需要往后移动
关于数组的更多解释,你可以参考这篇文章:数组的底层原理
1.1 类图
在IDEA中按下ctrl + alt + u
的组合键,我们可以查看到ArrayList的类图。
这里对UML图形做简单介绍:
实线三角箭头:继承(图中,蓝色为类的继承,绿色为接口的继承)
虚线三角箭头:实现
关于继承和实现此处不做过多的解释,现在我们描述下直接节点的几个含义:
-
RandomAccess
标志接口,表明实现这个接口的 List 集合是支持快速随机访问的,数组特性决定可以快速访问。
-
Cloneable
覆盖了函数
clone()
,能被克隆。 -
Serializable
支持序列化,能通过序列化去传输。
1.2 属性
2.基础知识
我们知道,List和Array的特性都是数据能有序存放,但与数组不同的是,List还可以动态扩容。
在ArrayList中,数据是通过内部的Object[]数组
来存放的。
2.1 System.arraycopy()
这是一个native方法,存放于java.lang包下,看名字我们应该能猜到,它能实现数组的复制,例如:
将arrA数组1号位开始的2个元素,复制到arrB数组2号位。
程序输出:
通过这个native方法,我们可以实现数组的扩容,比如现在我想把一个数组扩容到原来的两倍。
程序输出:
2.2 Arrays.copyOf()
以char为例,下面是源代码。
该方法其实就是调用了上面的native方法,返回了复制过后的数组。
其实实现模式跟我们自己写的一样,只不过它用的是复制的长度做了判断。
下面是一个示例:
程序输出:
3.构造方法
查看源码可以知道,我们在new ArrayList()的时候,是不会执行扩容操作的。
3.1 ArrayList()
3.2 ArrayList(int initialCapacity)
3.3 ArrayList(Collection<? extends E> c)
3.4 总结
通过构造方法可以看到,我们在new ArrayList()时,是不会触发扩容操作的。
构造时,无非就是给内部的elementData这个对象数组(属性)赋值。不必困扰transient
关键字,就是当前字段不参与序列化的标识。
放张小图,红色部分代表数据值被修改(write),绿色值代表数据值被使用(read)。
如何配置可以参考我这篇文章:IDEA中高亮显示变量的使用位置和赋值位置
4.扩容条件
ArrayList的扩容操作是在add()时进行触发的,我们可以查看add方法的源码。
在add开始前,会执行ensureCapacityInternal(size + 1)和elementData[size++] = e两个操作,然后返回true。
4.1 void ensureCapacityInternal(int minCapacity)
确保内部容量不小于minCapacity
ensureCapacityInternal方法又关联了ensureExplicitCapacity和calculateCapacity两个方法。
我们先对calculateCapacity方法做解释。
4.2 int calculateCapacity(Object[] elementData, int minCapacity)
计算所需容量
该方法用于计算容量,传入一个数组和int值。
什么时候会是空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA呢?返回上面的构造方法处可以看到是在无参构造中使用到了。
- elementData是无参构造器初始化的,返回值是minCapacity或者默认容量10。
- elementData不是无参构造器初始化的,返回值是minCapacity。
4.3 void ensureExplicitCapacity(int minCapacity)
确保显示容量不小于minCapacity
从上面4.1处可以看到,这个方法中形参的值(int minCapacity)是由calculateCapacity()方法计算来的。
如果,所需要的容量大于当前对象数组的长度,就执行扩容操作。
4.4 总结
现在我们把3个方法串起来,在进行add()操作时,必然会触发首行的ensureCapacityInternal()方法,传递的参数值是size+1
。
size是什么?size就是当前arrayList对象的元素个数,我们不是经常使用list.size()吗?在arrayList的源码中,它什么也没做,就是一个简单的get方法。
所以,在add()操作时,ensureCapacityInternal()方法传入了当前元素个数+1的值进去。
add()操作又是干嘛的?将元素添加到list的末尾。
我们要放元素到末尾,那么必须得先确保size+1
这个节点是可用的,即用来存放数据的对象数组最小容量是size+1
。
现在,我们需要根据这个最小容量minCapacity做后续操作,即ensureExplicitCapacity()方法。
如果当前对象数组的长度小于所需最小容量,则执行扩容操作。
5.扩容操作
扩容操作是通过grow(minCapacity)方法完成的。
__EOF__

本文链接:https://www.cnblogs.com/yang37/p/17087701.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具