ArrayList扩容机制

简介: ArrayList 添加元素时,首先调用 `ensureCapacityInternal()` 确保容量足够。首次添加时,最小容量设为默认值10,触发扩容;后续添加若超出当前容量(初始10,每次扩容1.5倍),则调用 `grow()` 扩容。`grow()` 将容量增加50%,并通过 `Arrays.copyOf()` 创建新数组。注意:`length` 用于数组,`length()` 用于字符串,`size()` 用于集合。

image.png
再来看看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);
}

image.png
我们来仔细分析一下

  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 倍!

通过例子探究一下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() 方法是针对泛型集合说的,如果想看这个泛型有多少元素,就调用此方法类查看!

相关文章
|
6月前
|
运维 安全 Ubuntu
补丁别靠吼,Linux补丁要自动化!从 openEuler 打通到全栈实践方案
补丁别靠吼,Linux补丁要自动化!从 openEuler 打通到全栈实践方案
463 154
|
5月前
|
传感器 安全 前端开发
USB专用过压保护ic芯片选型指南
平芯微电子推出高性能过压过流保护芯片系列,涵盖OVP/OCP双重防护、超低内阻、宽压可调等创新技术,提供从消费电子到车载系统的全场景电源保护方案,助力提升产品可靠性与竞争力。
|
5月前
|
边缘计算 安全 调度
游戏盾源码技术架构解析:分布式防护在实时交互应用中的实践
游戏盾是专为C/S架构设计的分布式安全防护方案,创新性将防护能力下沉至客户端。采用“客户端+调度层+边缘节点”三层架构,集成动态虚拟化、端到端加密与AI行为分析,实现抗DDoS、防CC攻击、通信防劫持等多重防护,兼顾安全性与低延迟体验,适用于游戏、直播、金融等高安全需求场景,支持多平台快速接入,已开源核心引擎供技术共建。
342 1
|
7月前
|
人工智能 运维 监控
从代码到生产推理服务:DevPod 全流程部署 DeepSeek-OCR 模型实战指南
DevPod重塑AI开发范式,实现从云端开发、调试到生产部署的全流程闭环。依托预置环境与GPU资源,一键完成模型服务化,打通AI落地“最后一公里”,让开发者专注业务创新。
|
5月前
|
缓存 网络协议 Linux
RARP协议详解(Linux下反向地址解析协议入门指南)
RARP(反向地址解析协议)是一种早期网络协议,用于通过MAC地址查询IP地址,常用于无盘工作站启动时获取IP。与ARP相反,RARP实现从MAC到IP的映射。尽管现已被DHCP取代,了解RARP仍对学习Linux网络配置和网络协议演进具有重要意义。
|
7月前
|
JavaScript Java 关系型数据库
基于springboot的图书馆座位预约系统
针对高校图书馆座位紧张与管理低效问题,本研究设计并实现了一套基于Spring Boot、Vue.js与MySQL的智能预约系统。系统通过移动端实现座位实时查询、预约、签到及违规管理,提升资源利用率与用户体验。采用Java语言开发,结合前后端分离架构,支持高并发访问,解决传统人工管理排队久、监管难等问题。对比国内外现有方案,本系统在智能化分配、稳定性与可扩展性方面更具优势,助力智慧校园建设,具有良好的应用推广价值。
|
5月前
|
API 数据安全/隐私保护 计算机视觉
用Python批量处理图片,5分钟搞定一天的工作
用Python批量处理图片,5分钟搞定一天的工作
482 128
|
5月前
|
机器学习/深度学习 算法 安全
基于yolov8深度学习的农作物识别检测系统
本研究基于YOLOv8深度学习技术,构建农作物识别检测系统,旨在实现对作物种类、生长状态及病虫害的快速精准识别。通过Python与先进算法结合,提升农业智能化水平,助力精准施肥、减少农药使用,推动农业可持续发展,具有重要应用价值。
|
5月前
|
Web App开发 前端开发 JavaScript
一次远程会议中我用到的 Chrome DevTools 调试技巧
本文由“喵喵侠”分享三个被忽视的Chrome DevTools实用技巧:删除遮挡元素、撤销误删操作、快速搜索DOM节点。通过真实场景演示,帮助开发者提升调试效率,避免因小失误影响协作,适合前端新手和非开发人员学习掌握。
188 0
一次远程会议中我用到的 Chrome DevTools 调试技巧