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

相关文章
|
9月前
|
Java 关系型数据库 MySQL
基于springboot的二手物品交易系统
本研究聚焦二手交易平台的网络化转型,探讨其在社会经济快速发展背景下的必要性与意义。结合SpringBoot、Java、MySQL等技术,分析系统设计与实现路径,旨在提升平台管理效率、降低成本,推动二手交易向规范化、信息化发展,助力现代化服务体系建设。
|
9月前
|
机器学习/深度学习 人工智能 自然语言处理
构建AI智能体:六十七、超参数如何影响大模型?通俗讲解原理、作用与实战示例
超参数是机器学习模型训练前需要人工设定的参数,它们控制着模型的学习过程而非直接通过学习获得。文章通过生动的类比(如自行车调整、烹饪配方)解释了超参数的概念,并详细介绍了其调优流程、常见类型(学习率、批量大小等)及对模型的影响。通过实际代码示例,展示了不同超参数设置如何影响模型训练效果,强调合理调优对提升模型性能、防止过拟合和优化资源使用的重要性。文章指出,超参数调优是模型成功的关键,初学者可从默认值开始逐步实验,借助网格搜索等工具实现高效调参。
833 105
|
9月前
|
机器学习/深度学习 数据采集 人工智能
构建AI智能体:七十二、交叉验证:从模型评估的基石到大模型时代的演进
交叉验证是机器学习中评估模型性能的核心方法,通过轮换数据划分实现稳健评估。文章系统解析了k折交叉验证的原理与实现,展示其在模型比较和超参数优化中的关键作用。随着大模型时代的到来,传统交叉验证面临计算成本挑战,但核心思想仍应用于下游任务。文章通过可视化案例完整呈现了从数据准备到最终评估的工作流程,强调交叉验证在有限数据场景下的不可替代性,同时指出需要根据任务规模灵活选择评估策略。理解交叉验证的原理与应用是AI从业者的必备能力。
858 16
|
8月前
|
机器学习/深度学习 数据采集 运维
实现MATLAB滚动轴承故障诊断
核心流程 “信号采集→预处理→特征提取→故障识别”,其中特征提取是连接原始信号与故障诊断的关键环节。
|
9月前
|
人工智能 前端开发 搜索推荐
AI聊天居然有17种姿势?提示工程师的武功秘籍大公开
想让ChatGPT更听话?别只会说'请帮我...'了!从零样本到思维树,从检索增强到自动推理,17种提示工程技术让你的AI助手从'憨憨'变'大神'。掌握这些技巧,告别低效对话,让AI真正为你所用!#人工智能 #提示工程 #ChatGPT #大模型
698 11
|
9月前
|
开发框架 Cloud Native JavaScript
阿里云轻量应用服务器:值得买的云服务器推荐
阿里云轻量应用服务器全新升级,200Mbps峰值带宽、不限流量,支持多公网IP与丰富应用镜像,如WordPress、宝塔等,一键部署。适用于建站、跨境电商、游戏加速等场景,性价比高,68元起/年,限时抢购低至38元,中小企业和开发者首选。
753 12
@Documented注解
该注解可用于生成Javadoc文档,结合@Target、@Retention等元注解,是实现自定义注解的基础。掌握其用法可提升代码可读性与开发效率。详情可参考“自定义注解”教程。
|
9月前
|
Java 测试技术 Apache
安装Jmeter
JMeter依赖JDK,需先安装并配置JDK环境变量。前往Apache JMeter官网(http://jmeter.apache.org/download_jmeter.cgi)下载最新版本,解压后即可使用,适用于性能测试与负载模拟。

热门文章

最新文章