ArrayList扩容机制

简介: ArrayList添加元素时,先调用ensureCapacityInternal()确保容量,首次添加时默认扩容至10。每次扩容通过grow()实现,新容量为原容量的1.5倍(oldCapacity + (oldCapacity >> 1)),提升性能。add第11个元素时再次触发扩容。length为数组属性,length()是字符串方法,size()用于集合获取元素数。

● 先来看Add方法
/* 将指定的元素追加到此列表的末尾
*/
public boolean add(E e) {
//添加元素之前,先调用ensureCapacityInternal方法
ensureCapacityInternal(size + 1); // Increments modCount!!(增量modCount)
//这里看到ArrayList添加元素的实质就相当于为数组赋值
elementData[size++] = e;
return true;
}
● 再来看看ensureCapacityInternal()方法,可以看到add()方法首先调用了ensureCapacityInternal(size+1)
//得到最小扩容量
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//获取默认的容量和传入参数的较大值(第一次的较大值是DEFAULT_CAPACITY=10,minCapacity=1)
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
当要add进第一个元素时,minCapacity为1,在Math.max()方法比较后,minCapacity为10
● ensureExplicitCapacity()方法
//判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//调用grow()方法进行扩容,调用此方法代表已经开始扩容了
grow(minCapacity);
}
我们来仔细分析一下

  1. 当我们要add进第一个元素到ArrayList时,elementData.length为0(因为还是一个空的list,里面还没有数据,所以没有进行扩容,默认扩容10),因为执行了ensureCapacityInternal()方法,所以minCapacity此时为10。此时,minCapacity - elemetData.length > 0(minCapacity=10,elemetData.length=0)成立,所以会进入==grow(minCapacity)==方法。
  2. 当add第2个元素时,minCapacity为2,此时elementData.length(容量)在添加第一个元素后扩容成10了。此时,minCapacity - elementData.length > 0不成立,所以不会进入(执行)==grow(minCapacity)==方法。
  3. 添加第3、4…到第10个元素时,依然不会执行==grow()==方法,数组容量都为10。
    知道添加第11个元素,minCapacity(为11)比elementData.length(为10)要大。进行grow方法进行扩容
  4. grow方法
    private void grow(int minCapacity) {
    // oldCapacity为旧容量,newCapacity为新容量
    int oldCapacity = elementData.length;//(0,10,15)
    //将oldCapacity右移一位,其效果相当于oldCapacity/2;
    // 我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么久把最小需要容量当作数组的新容量
    if (newCapacity - minCapacity < 0)
     newCapacity = minCapacity;
    
    //判断新容量是否大于集合的最大容量(一般大不了)
    if (newCapacity - MAX_ARRAY_SIZE > 0)
     newCapacity = hugeCapacity(minCapacity);
    
    // 给elementData从新赋值(10,15)
    elementData = Arrays.copyOf(elementData, newCapacity);
    }
    int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍!
    “>>”(移位运算符):>>1 右移一位相当于除2,右移n位相当于除以 2 的 n 次方。这里 oldCapacity 明显右移了1位所以相当于oldCapacity /2。对于大数据的2进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源
    通过例子探究一下grow()方法
    ● 当add第一个元素时,oldCapacity为0,经比较后第一个if判断成立,newCapacity = minCapacity(为10)。但是第二个if判断不会成立,即newCapacity不比MAX_ARRAY_SIZE大,则不会进入hugeCapacity方法。数组容量为10,add方法中return true,size增为1。
    ● 当add第11个元素进入grow方法时,newCapacity为15,比minCapacity(为11)大,第一个if判断不成立。新容量没有大于数组最大size,不会进入hugeCapacity方法。数组容量扩为15,add方法中rerurn,true,size增为11。
    ● 以此类推…
    这里补充一点比较重要,但是容易被忽视掉的知识点:
    ● java中的length属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了length这个属性。
    ● java中的length() 方法是针对字符串说的,如果想看这个字符串的长度则用到 length() 这个方法。
    ● java中的size() 方法是针对泛型集合说的,如果想看这个泛型有多少元素,就调用此方法类查看!
相关文章
|
3月前
|
存储 监控 Java
整合切面,参数拦截+过滤
该类基于Spring AOP实现请求参数日志记录,通过`@Before`、`@Around`和`@After`切面拦截Controller层方法,自动记录请求来源、URL、方式、参数及执行耗时,便于调试与监控,日志通过LogProxy输出,提升系统可观测性。(238字)
|
3月前
|
Arthas 存储 运维
记Arthas实现一次CPU排查与代码热更新
本文介绍如何使用Arthas排查线上Java应用CPU占用过高问题,结合thread、watch、jad等指令定位阻塞线程与异常代码,实现无需重启服务的热更新修复,并通过profile生成火焰图进行性能分析,提升线上问题排查效率。
|
3月前
|
缓存 前端开发 安全
数据同步原理
Soul网关通过推拉模式实现配置数据同步,支持WebSocket、HTTP长轮询和Zookeeper三种策略。管理员在后台变更配置后,事件被发布并根据同步策略推送到网关,实现秒级更新。HTTP长轮询借鉴Apollo与Nacos设计,结合异步Servlet机制,确保准实时且高效。
数据同步原理
|
3月前
|
Java Shell Apache
Jmeter快速入门
本文介绍了JMeter的安装与快速入门指南。首先需确保已安装JDK并配置环境变量,随后从官网或本地包下载JMeter,解压后通过双击或命令行启动。教程还演示了如何设置中文界面、创建线程组、添加HTTP取样器及监听器,并查看测试结果,帮助用户快速掌握基本使用方法。
Jmeter快速入门
|
3月前
|
Java
常见加载顺序
本示例展示了Java中各类代码块的执行顺序:静态代码块随类加载仅执行一次,优先于主函数;局部代码块在方法内直接运行;构造代码块每次创建对象前自动执行,早于构造器。输出结果体现三者优先级:静态 &gt; 局部 &gt; 构造。
|
3月前
|
消息中间件 算法 网络协议
8.选举机制理解描述
本文深入解析ZooKeeper的Leader选举机制,涵盖启动期与运行期的选举流程、FastLeaderElection算法核心规则及实现细节,重点阐述投票PK规则、逻辑时钟作用与网络通信机制,确保分布式系统数据一致性。
|
3月前
|
存储 人工智能 运维
UModel PaaS API 架构设计与最佳实践
UModel PaaS API 通过“表-对象-元数据”三层抽象,屏蔽底层复杂性,统一可观测数据访问。支持 SPL 一键查询、实体方法调用与 AI 自主探索,降低开发门槛,提升运维效率。
|
3月前
|
XML SQL 监控
整合Logback,滚动记录+多文件
`logback-spring.xml` 配置了多模块日志分离输出,按类别将支付、任务、SQL、错误等日志写入不同文件,支持滚动策略与UTF-8编码。通过 `LogProxy.getLogger(&quot;LOG_NAME&quot;)` 获取指定日志器,实现精准日志记录,便于问题追踪与系统监控。(236字符)
|
3月前
|
JSON Java Maven
SpringBoot使用汇总
Spring Boot是Spring框架的延伸,旨在简化Spring应用的初始搭建与开发过程。它通过自动配置、内嵌服务器、开箱即用的依赖等方式,极大减少了项目配置和编码量,提升开发效率。支持快速构建微服务,是Java EE开发的主流趋势。
|
3月前
|
Java 调度
线程池
线程池通过复用线程提升性能,避免频繁创建销毁的开销。Java中由Executor框架实现,核心为ThreadPoolExecutor,管理线程生命周期与任务调度。通过Executors工厂创建,支持提交异步任务、定时执行等。关键组件包括工作队列、线程工厂与拒绝策略,实现高效并发控制。(238字)