【Java】ArrayList数组的扩容机制 jdk1.8

  📝个人主页:哈__

期待您的关注 

ArrayList和普通数组不同,ArrayList支持动态扩容,那么ArrayList到底是如何扩容的呢?你又是否知道ArrayList数组的初始长度是多少呢?

在开始介绍之前,我们要先介绍一下ArrayList类中的一些属性。

    /*** *默认初始容量。*/private static final int DEFAULT_CAPACITY = 10;/*** 用于空实例的共享空数组实例。*/private static final Object[] EMPTY_ELEMENTDATA = {};/*** 共享的空数组实例用于默认大小的空实例。我们* 将其与EMPTY_ELEMENTDATA区分开来,以了解何时膨胀多少* 添加第一个元素。*/private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};/*** 存放数组列表元素的数组缓冲区。* 数组列表的容量是这个数组缓冲区的长度。任何空的数组且满足elementData == * DEFAULTCAPACITY_EMPTY_ELEMENTDATA* 将在添加第一个元素时扩展为DEFAULT_CAPACITY。*/transient Object[] elementData; 

elementData就是我们的数据要存储的进入的数组,看上边的注释说,如果数组是空的并且满足elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA的时候,数组就会被扩容为10;

那么接下来我们看一下ArrayList的三个构造方法。

1.有参构造方法

// 传入参数初始化数组的大小
public ArrayList(int initialCapacity) {//如果初始化的大小大于0if (initialCapacity > 0) {//为elementData初始化this.elementData = new Object[initialCapacity];//如果初始化的空间大小为0} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}

2.无参构造方法

public ArrayList() {// elementData数组就等于DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}

3.传入Collention元素列表

/**
*按照指定集合的迭代器返回的顺序,构造一个包含指定集合元素的列表。
**/
public ArrayList(Collection<? extends E> c) {//将元素列表转为数组Object[] a = c.toArray();//如果元素个数不为0if ((size = a.length) != 0) {//如果元素列表是一个ArrayList类型if (c.getClass() == ArrayList.class) {//把a赋给elementDataelementData = a;} else {// 如果不是ArrayList类型,进行元素拷贝elementData = Arrays.copyOf(a, size, Object[].class);}} else {// 如果元素个数为0,将elementData赋值为空数组elementData = EMPTY_ELEMENTDATA;}}

ArrayList的扩容机制

我们向ArrayList中添加数据时,调用的是add()方法。我们跟进查看。首先执行ensureCapacityInternal(size + 1); 这个方法,翻译为确保内部容量,传入的参数是当前集合的元素个数再加上1,一定是加1,这样才能确保正确的添加。我们跟进到方法中查看。

public boolean add(E e) {//确保内部容量 传入当前集合中的元素个数在加上1ensureCapacityInternal(size + 1); elementData[size++] = e;return true;}

这就是这样的一个确保内部容量的方法,传入了一个参数,名为最小的容量,之后调用 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity))这个方法,但是这个方法中还有一个calculateCapacity(elementData, minCapacity)方法,我们先进入这个方法查看

private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}

 calculateCapacity方法如下。他需要两个参数,一个是elementData,一个是最小的容量。如果我们的elementDataDEFAULTCAPACITY_EMPTY_ELEMENTDATA这个数组的话,那么我们就返回这个最小的容量和我们内部默认容量中大的一个。如果不是这个默认数组的话直接返回最小容量。这里的判断是因为我们有两种不同的构造函数,一个是无参,另一个是有参,无参构造函数在添加数据的时候会自动将数组扩容为10。

private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;}

这样调用完这个函数之后我们就知道数组的容量是多少了。

我们返回ensureExplicitCapacity这个函数接着看。

他需要一个参数就是最小的容量。modCount记录的是数组的修改次数。

接着判断最小的容量减去我们当前数组的容量,如果数组的空间不够,我们就要的调用grow函数进行扩容。否则的话我们就直接回到了最上方的add函数当中进行元素添加。

private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);}

grow函数如下。别慌我写了注释。

private void grow(int minCapacity) {// overflow-conscious code// 这是我们之前数组的容量int oldCapacity = elementData.length;// 新数组的容量应该是旧数组容量+旧数组容量/2,也就是扩容了1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);// 如果新的容量还是不够,那我们就直接把容量定为传入的最小容量if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 如果扩容扩出的新数组太大了,比数组最大长度还要大 要重新扩容if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// 将我们的elementData进行扩容,然后赋值给我们的elementData数组,也就是把数组搬到了一个// 大的空间中elementData = Arrays.copyOf(elementData, newCapacity);}

如果真的走到了hugeCapacity函数中,如下所示。

 private static int hugeCapacity(int minCapacity) {// 如果最小的容量小于0 直接报错if (minCapacity < 0) // overflowthrow new OutOfMemoryError();// 如果最小容量比数组最大容量大,返回整形的最大值,否则的话就是等于数组最大值// 不再扩充1.5倍了return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}

到了这一步真的就是走完了最最最上方的ensureCapacityInternal方法,然后就可以添加元素了。

以上内容就是ArrayList集合的扩容机制。

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

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

相关文章

代码随想录阅读笔记-二叉树【翻转二叉树】

题目 翻转一棵二叉树。 思路 如果要从整个树来看&#xff0c;翻转还真的挺复杂&#xff0c;整个树以中间分割线进行翻转&#xff0c;如图&#xff1a; 可以发现想要翻转它&#xff0c;其实就把每一个节点的左右孩子交换一下就可以了。 关键在于遍历顺序&#xff0c;前中后序应…

2024年妈妈杯数学建模思路A题B题C题D题思路分享

文章目录 1 赛题思路2 比赛日期和时间3 组织机构4 建模常见问题类型4.1 分类问题4.2 优化问题4.3 预测问题4.4 评价问题 5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 比赛日期和时间 报名截止时间&#xff1a;2024…

​马来语翻译中文去哪比较好?

据了解&#xff0c;马来语是马来西亚、文莱的官方语言&#xff0c;也是新加坡的官方语言之一&#xff0c;马来语跟印尼语是同一种语言&#xff0c;它在整个东南亚有着极大的影响力。如今在国内市场上&#xff0c;马来语翻译的需求也是供不应求&#xff0c;那么&#xff0c;如何…

信息安全之网络安全防护

先来看看计算机网络通信面临的威胁&#xff1a; 截获——从网络上窃听他人的通信内容中断——有意中断他人在网络上的通信篡改——故意篡改网络上传送的报文伪造——伪造信息在网络上传送 截获信息的攻击称为被动攻击&#xff0c;而更改信息和拒绝用户使用资源的攻击称为主动…

利用Python和IP技术实现智能旅游情报系统

文章目录 引言一、系统架构设计1. 数据采集模块2. 数据处理模块3. 用户界面模块 二、数据获取技术应用三、系统功能展示四、亮数据采集工具介绍五、总结六、号外 引言 随着旅游行业的不断发展&#xff0c;人们对旅游信息的需求也越来越大。为了帮助旅行者更好地规划行程&#…

智慧酒店(二):AI智能分析网关V4视频分析技术在酒店管理中的应用

一、人工智能技术如何应用在酒店管理中&#xff1f; 随着科技的飞速发展&#xff0c;人工智能技术已经逐渐渗透到我们生活的方方面面&#xff0c;其中&#xff0c;酒店管理行业便是其应用的重要领域之一。人工智能技术以其高效、精准的特点&#xff0c;为酒店管理带来了革命性…

前端超分辨率技术应用:图像质量提升与场景实践探索-设计篇

超分辨率&#xff01; 引言 在数字化时代&#xff0c;图像质量对于用户体验的重要性不言而喻。随着显示技术的飞速发展&#xff0c;尤其是移动终端视网膜屏幕的广泛应用&#xff0c;用户对高分辨率、高质量图像的需求日益增长。然而&#xff0c;受限于网络流量、存储空间和图像…

【报错解决】RuntimeError: CUDA error: out of memory(已解决)

【报错解决】RuntimeError: CUDA error: out of memory&#xff08;已解决&#xff09; 【先赞后看养成习惯】求关注点赞收藏 问题描述&#xff1a;在运行python代码中&#xff0c;遇到如下报错 return t.to(device, dtype if t.is_floating_point() or t.is_complex() else N…

153 Linux C++ 通讯架构实战8 ,日志打印实战,设置时区,main函数中顺序调整

日志打印实战 //日志的重要性&#xff1a;供日后运行维护人员去查看、定位和解决问题&#xff1b; //新文件&#xff1a;ngx_printf.cxx以及ngx_log.cxx。 //ngx_printf.cxx&#xff1a;放和打印格式相关的函数&#xff1b; //ngx_log.cxx&#xff1a;放和日志相关…

面向对象-接口

面向对象-接口 1. 接口的概念 ​ 接口就是规范\规则&#xff0c;我们可以使用接口来定义一些规则&#xff08;比如要求某个方法的方法名必须叫什么&#xff0c;方法的参数列表必须是什么&#xff0c;方法的返回值类型必须是什么&#xff09; ​ 现实生活中的规范 2. 接口的…

Jenkins拉取github项目相关问题

1.私有仓库问题 1.1如果你的仓库是私有的&#xff0c;21年起github就不支持账号密码的方式拉取代码了 那么就需要在github上面创建一个token (classic) 然后在Jenkins代码设置那里 然后应该就可以顺利打包了。 2.找不到pom&#xff08;多了一层文件夹&#xff09;问题 解…

【Flink】Flink 处理函数之基本处理函数(一)

1. 处理函数介绍 流处理API&#xff0c;无论是基本的转换、聚合、还是复杂的窗口操作&#xff0c;都是基于DataStream进行转换的&#xff0c;所以统称为DataStreamAPI&#xff0c;这是Flink编程的核心。 但其实Flink为了更强大的表现力和易用性&#xff0c;Flink本身提供了多…