ArrayList 扩容机制的源码剖析

        在 Java 编程中,ArrayList是一个常用的集合类,它实现了List接口,底层基于数组实现。与普通定长数组不同,ArrayList能够根据元素的添加情况动态调整数组的大小,这就是其扩容机制。下面我们将深入剖析ArrayList扩容机制的源码。

        有的同学可能在别的地方听过一些ArrayList扩容,大部分会说ArrayList底层是数组结构的,默认长度为10,当数组加满后会自动扩容1.5倍。这样的说法不完全对。

        正确的步骤应该是如下的:        

一、ArrayList的构造方法

1. 无参构造

          我们可以自己实际的去探索一下,打开idea,按快捷键ctrl+n,输入ArrayList,选择All Places,点击Java.util那个。

        进入之后可以按ctrl+f12,按add,查找add方法,可以发现有很多add方法。

或者按Alt+7将整个大纲罗列出来,这里我们主要看的是空参构造

在这选中elementDate,按住ctrl+B,发现它是一个数组。

我们再看看DEFAULTCAPACITY_EMPTY_ELEMENTDATA这个是什么,就会发现它是一个长度为0的数组,所以空参构造默认初始值为0。

当使用new ArrayList<>()创建对象时,底层会创建一个空数组。相关源码如下:

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

这里的DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个空的Object数组,ArrayList将其赋值给用于存储元素的elementData数组。

这里我将所有代码类全部汇总到一起,可以更好的理解过程。

2. 带初始容量的构造

当使用new ArrayList<>(int size)时,如果传入的size大于 0,会创建一个指定长度的数组;如果size等于 0,会创建一个空数组,与无参构造的情况相同;如果size小于 0,则会抛出IllegalArgumentException异常。源码如下:

public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}
}

其中EMPTY_ELEMENTDATA也是一个空数组,与DEFAULTCAPACITY_EMPTY_ELEMENTDATA有所区别,主要用于标识通过带容量参数且容量为 0 的构造方法创建的情况。

3. 带集合参数的构造

当使用new ArrayList<>(Collection<? extends E> c)时,会先将传入的集合转换为数组,然后判断数组长度:如果长度为 0,按无参构造方式创建空数组;如果不为 0,再判断传入的集合是否是ArrayList类型。如果是,直接将转换后的数组赋值给elementData;如果不是,使用Arrays.copyOf方法进行二次复制,以确保安全性和隔离性。相关源码如下:

public ArrayList(Collection<? extends E> c) {elementData = c.toArray();if ((size = elementData.length) != 0) {if (c.getClass() == ArrayList.class) {this.elementData = elementData;} else {this.elementData = Arrays.copyOf(elementData, size, Object[].class);}} else {this.elementData = EMPTY_ELEMENTDATA;}
}

二、添加元素与扩容触发

当调用add(E e)方法向ArrayList中添加元素时,可能会触发扩容机制。add方法的源码如下:

public boolean add(E e) {ensureCapacityInternal(size + 1);  // 确保有足够的容量来存储新元素elementData[size++] = e;return true;
}

其中ensureCapacityInternal(int minCapacity)方法用于确保内部数组有足够的容量来存储新元素。minCapacity表示所需的最小容量,这里是当前元素个数size加 1。

进入ensureCapacityInternal方法:

private void ensureCapacityInternal(int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);}ensureExplicitCapacity(minCapacity);
}

在这个方法中,如果elementData是默认的空数组(即通过无参构造创建的情况),会将minCapacity设置为默认容量DEFAULT_CAPACITY(值为 10)和minCapacity中的较大值。然后调用ensureExplicitCapacity方法来判断是否需要扩容。

ensureExplicitCapacity方法的源码如下:

private void ensureExplicitCapacity(int minCapacity) {modCount++;if (minCapacity - elementData.length > 0)grow(minCapacity);
}

这里通过比较所需的最小容量minCapacity和当前数组elementData的长度,如果minCapacity大于elementData.length,则说明当前数组容量不足,需要调用grow方法进行扩容。

三、扩容的核心方法grow

grow方法是ArrayList扩容的关键,其源码如下:

private void grow(int minCapacity) {int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);  // 扩容为原来的1.5倍if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);elementData = Arrays.copyOf(elementData, newCapacity);
}

grow方法中,首先获取当前数组的容量oldCapacity,然后通过oldCapacity + (oldCapacity >> 1)计算出一个新的容量newCapacity,这里oldCapacity >> 1表示将oldCapacity右移一位,相当于除以 2,所以新容量是原来的 1.5 倍。

接着会进行两次判断:

  1. 如果newCapacity小于minCapacity,说明 1.5 倍扩容后的容量仍不满足需求,此时将newCapacity设置为minCapacity,即按需扩容。
  2. 如果newCapacity超过了ArrayList所能支持的最大数组大小MAX_ARRAY_SIZE(在Integer.MAX_VALUE - 8Integer.MAX_VALUE之间的一个值,具体取决于 JVM 的实现),则调用hugeCapacity方法来确定最终的容量。

最后,通过Arrays.copyOf方法将原数组的元素复制到新的、更大容量的数组中,完成扩容操作。

ArrayList的扩容机制通过巧妙的设计和源码实现,在保证能够动态存储元素的同时,尽量减少不必要的数组复制操作,提高了性能。深入理解其扩容机制,有助于我们在使用ArrayList时更好地进行性能优化和资源管理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/69095.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

The specified Gradle distribution ‘gradle-bin.zip‘ does not exist.

The specified Gradle distribution ‘https://services.gradle.org/distributions/gradle-bin.zip’ does not exist. distributionUrl不存在&#xff0c;关联不上&#xff0c;下载不了&#xff0c;那就匹配一个能下载的 distributionUrlhttps://services.gradle.org/distrib…

从零开始实现一个双向循环链表:C语言实战

文章目录 1链表的再次介绍2为什么选择双向循环链表&#xff1f;3代码实现&#xff1a;从初始化到销毁1. 定义链表节点2. 初始化链表3. 插入和删除节点4. 链表的其他操作5. 打印链表和判断链表是否为空6. 销毁链表 4测试代码5链表种类介绍6链表与顺序表的区别7存储金字塔L0: 寄存…

Cesium点集中获取点的id,使用viewer.value.entities.getById报错的解决方法

错误代码&#xff1a; viewer.value.entities.getById(pickedObject.id) 报错&#xff1a; 可以正常获取movement.position但是一直出现如下报错&#xff0c;无法获得航点的id&#xff0c;通过断点定位为 viewer.value.entities.getById(pickedObject.id)导致的报错 解决方…

ARM Linux Qt使用JSON-RPC实现前后台分离

文章目录 1、前言2、解决方案2.1、JSON-RPC2.2、Qt中应用JSON-RPC的框架图2.3、优点2.4、JSON-RPC 1.0 协议规范 3、程序示例3.1、Linux C&#xff08;只例举RPC Server相关程序&#xff09;3.2、Qt程序&#xff08;只例举RPC Client相关程序&#xff09; 4、编译程序4.1、交叉…

教程 | i.MX RT1180 ECAT_digital_io DEMO 搭建(一)

本文介绍 i.MX RT1180 EtherCAT digital io DEMO 搭建&#xff0c;Master 使用 TwinCAT &#xff0c;由于步骤较多&#xff0c;分为上下两篇&#xff0c;本文为第一篇&#xff0c;主要介绍使用 TwinCAT 控制前的一些准备。 原厂 SDK 提供了 evkmimxrt1180_ecat_examples_digit…

ubuntu22.40安装及配置静态ip解决重启后配置失效

遇到这种错误&#xff0c;断网安装即可&#xff01; 在Ubuntu中配置静态IP地址的步骤如下。根据你使用的Ubuntu版本&#xff08;如 Netplan 或传统的 ifupdown&#xff09;&#xff0c;配置方法有所不同。以下是基于 Netplan 的配置方法&#xff08;适用于Ubuntu 17.10及更高版…

【产品经理学习案例——AI翻译棒出海业务】

前言&#xff1a; 本文主要讲述了硬件产品在出海过程中&#xff0c;翻译质量、翻译速度和本地化落地策略是硬件产品规划需要考虑的核心因素。针对不同国家&#xff0c;需要优化翻译质量和算法&#xff0c;关注市场需求和文化差异&#xff0c;以便更好地满足当地用户的需求。同…

CH340G上传程序到ESP8266-01(S)模块

文章目录 概要ESP8266模块外形尺寸模块原理图模块引脚功能 CH340G模块外形及其引脚模块引脚功能USB TO TTL引脚 程序上传接线Arduino IDE 安装ESP8266开发板Arduino IDE 开发板上传失败上传成功 正常工作 概要 使用USB TO TTL&#xff08;CH340G&#xff09;将Arduino将程序上传…

AI推理性能之王-Groq公司开发的LPU芯片

Groq公司开发的LPU&#xff08;Language Processing Unit&#xff0c;语言处理单元&#xff09;芯片是一种专为加速大规模语言模型&#xff08;LLM&#xff09;和其他自然语言处理任务而设计的新型AI处理器。以下是对其技术特点、性能优势及市场影响的深度介绍&#xff1a; 技…

rk3506 sd卡启动

1 修改系统配置文件,打开ext4 #SDMMC RK_ROOTFS_TYPE"ext4" RK_ROOTFS_INSTALL_MODULESy RK_WIFIBT_CHIP"AIC8800" # RK_ROOTFS_LOG_GUARDIAN is not set RK_UBOOT_CFG_FRAGMENTS"rk3506_tb" RK_UBOOT_SPLy RK_KERNEL_CFG"rk3506_defconfi…

Docker 安装详细教程(适用于CentOS 7 系统)

目录 步骤如下&#xff1a; 1. 卸载旧版 Docker 2. 配置 Docker 的 YUM 仓库 3. 安装 Docker 4. 启动 Docker 并验证安装 5. 配置 Docker 镜像加速 总结 前言 Docker 分为 CE 和 EE 两大版本。CE即社区版&#xff08;免费&#xff0c;支持周期7个月&#xff09;&#xf…

【玩转 Postman 接口测试与开发2_017】第13章:在 Postman 中实现契约测试(Contract Testing)与 API 接口验证(下)

《API Testing and Development with Postman》最新第二版封面 文章目录 第十三章 契约测试与 API 接口验证8 导入官方契约测试集合9 契约测试集合的详细配置9.1 env-apiKey 的创建与设置9.2 env-workspaceId 的设置9.3 Mock 服务器及 env-server 的配置9.4 API 测试实例的配置…

使用DeepSeek R1 + 了解部署

官网注册 R1模型&#xff0c;推理模型 参考视频理解 理解大语言模型的本质 大模型在训练时是将内容token化的大模型知识是存在截止时间的大模型缺乏自我认知、自我意识记忆有限输出长度有限 智商理解&#xff0c;例如下面的DeepSeek的测试&#xff1a; 用DeepSeek 官网手…

2024年12月 Scratch 图形化(三级)真题解析 中国电子学会全国青少年软件编程等级考试

202412 Scratch 图形化&#xff08;三级&#xff09;真题解析 中国电子学会全国青少年软件编程等级考试 一、选择题(共18题&#xff0c;共50分) 第 1 题 气温和对应的穿衣建议如下表所示&#xff0c;下列选项能正确给出穿衣建议的是&#xff1f;&#xff08; &#xff09; A. …

Gauss高斯:建表语法,存储方式,OLTP和OLAP,系统时间,数组,分组(grouping set,rollup)

数据库和表的语法 数据库 表 oracle,高斯, hive的默认存储方式都是列式存储 存储方式 高斯数据库&#xff08;GaussDB&#xff09;支持列式存储和行式存储 OLTP 与 OLAP OLTP&#xff08;联机事务处理&#xff0c;Online Transaction Processing&#xff09;是一种用于管理…

数据中心服务器对PCIe测试的需求、挑战和应用

人工智能和机器学习技术的迅猛发展&#xff0c;尤其是大语言模型&#xff08;LLM&#xff09;的兴起&#xff0c;对计算资源和数据传输速度提出了更高的要求&#xff0c;从而激发了对更高带宽解决方案的迫切需求。PCIe作为数据中心服务器间互联的主力军&#xff0c;承担着高速数…

(9)下:学习与验证 linux 里的 epoll 对象里的 EPOLLIN、 EPOLLHUP 与 EPOLLRDHUP 的不同。小例子的实验

&#xff08;4&#xff09;本实验代码的蓝本&#xff0c;是伊圣雨老师里的课本里的代码&#xff0c;略加改动而来的。 以下是 服务器端的代码&#xff1a; 每当收到客户端的报文时&#xff0c;就测试一下对应的 epoll 事件里的事件标志&#xff0c;不读取报文内容&#xff0c;…

【C语言篇】“三子棋”

一、游戏介绍 三子棋&#xff0c;英文名为 Tic - Tac - Toe&#xff0c;是一款简单而经典的棋类游戏。游戏在一个 33 的棋盘上进行&#xff0c;两名玩家轮流在棋盘的空位上放置自己的棋子&#xff08;通常用 * 和 # 表示&#xff09;&#xff0c;率先在横、竖或斜方向上连成三个…

wsl+phpstorm+xdebug|windows子系统配置phpstorm开发调试|断点调试

安装wsl 安装apache php 安装xdebug扩展&#xff0c;并配置 这里是通过宝塔9.4面板安装的xdebug3.0 [xdebug] xdebug.modedebug xdebug.start_with_requesttrue xdebug.discover_client_hosttrue xdebug.client_host127.0.0.1配置PHPSTORM 注意&#xff1a;新建服务器一定要…

20250204将Ubuntu22.04的默认Dash的shell脚本更换为bash

20250204将Ubuntu22.04的默认Dash的shell脚本更换为bash 2025/2/4 23:45 百度&#xff1a;dash bash https://blog.csdn.net/2201_75772333/article/details/136955776 【Linux基础】dash和bash简介 Dash&#xff08;Debian Almquist Shell&#xff09;和 Bash&#xff08;Bou…