ArrayList扩容机制

简介: 本文深入解析ArrayList的add及扩容机制。添加元素时,先调用ensureCapacityInternal()确保容量,首次默认扩容至10;每次扩容为原容量的1.5倍(通过位移运算优化),并结合modCount实现快速增容与赋值。同时辨析length、length()、size()的区别,帮助理解Java中不同数据类型的长度获取方式。
  • 先来看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() 方法是针对泛型集合说的,如果想看这个泛型有多少元素,就调用此方法类查看!
相关文章
|
SQL 存储 JSON
MySQL执行请求报错 Error: Row size too large (>8126)
最近遇到一个业务问题,在执行一个大的业务查询时会抛出异常报错,所以今天就总结一下 Row size too large (>8126) 报错的相关问题。
|
缓存 Linux Windows
Linux环境aspose插件word转pdf中文乱码解决方案
Linux环境aspose插件word转pdf中文乱码解决方案
3272 0
THW
|
11月前
|
存储 安全
BadUSB 攻击原理
BadUSB攻击通过将USB设备伪装成键盘等HID设备,利用系统对HID的信任,在连接电脑后自动模拟键盘输入执行恶意指令,从而绕过传统安全防护,实现无需用户操作的秒级入侵。
THW
1356 0
|
11月前
|
运维 网络协议 Shell
【网络运维】Ping不通?别慌!用Telnet和Netstat精准排查端口连通性
本文介绍网络排查两大利器:Telnet与Netstat。通过Ping只能检测服务器是否在线,而Telnet可测试端口连通性,区分“连接被拒”与“超时”;Netstat则用于服务端查看端口监听状态,识别如仅监听127.0.0.1导致外网无法访问的问题。结合ss命令和“三板斧”流程,快速定位网络故障。
|
11月前
|
关系型数据库 应用服务中间件 nginx
容器化部署引擎Docker
Docker是一种轻量级容器化技术,通过镜像打包应用及依赖,实现跨环境一致部署。它利用沙箱机制隔离容器,解决开发、测试、生产环境差异与组件兼容性问题,相比虚拟机更高效便捷,提升应用交付效率。
容器化部署引擎Docker
|
11月前
|
Kubernetes IDE 应用服务中间件
2.部署篇(开发部署)
本文介绍如何将SpringCloud应用部署到Kubernetes云端,基于EDAS实现快速上云。涵盖集群导入、应用初始化及通过IDE插件高效部署,助力开发者提升发布效率。
|
11月前
|
自然语言处理 监控 搜索推荐
分布式搜索引擎ElasticSearch
Elasticsearch是基于Lucene的开源分布式搜索引擎,支持全文检索、日志分析与实时监控,结合Logstash、Kibana等组成ELK技术栈,广泛应用于搜索、数据分析等领域。
分布式搜索引擎ElasticSearch
|
11月前
|
Kubernetes Java 应用服务中间件
1.开发篇(脚手架下载)
本文介绍基于Spring Cloud Alibaba与Kubernetes的微服务开发实践,重点分享项目初始化与本地启动的高效工具。通过阿里云EDAS 3.0及配套插件,开发者可快速搭建应用并自动对接注册中心,显著提升开发体验。后续将深入讲解云端部署与端云互联能力。
1.开发篇(脚手架下载)
|
11月前
|
数据采集 领域建模 数据库
领域模型图(数据架构/ER图)
本文介绍如何通过四色原型法进行领域建模,构建数据架构中的ER图。以风控系统为例,依次解析关键流程、识别时标性原型(MI)、参与方-地点-物品原型(PPT)、角色原型(Role)和描述原型(DESC),最终提炼出实体与关系,形成简洁的ER图,助力数据模型设计。
领域模型图(数据架构/ER图)
|
11月前
|
监控 Java 测试技术
微服务保护Sentinel
本课程深入讲解微服务中的雪崩问题及其解决方案,重点介绍阿里开源的流量治理组件Sentinel。内容涵盖Sentinel的部署与整合、限流模式(直接、关联、链路)、流控效果(快速失败、预热、排队等待)、熔断降级、线程隔离及规则持久化等核心知识点,结合Jmeter压测实战,帮助开发者构建高可用的分布式系统。