ArrayList常见操作源码逐句剖析

目录

前言

正文

1.需要了解的一些字段属性

1.存储 ArrayList 元素的数组缓冲区。

2.集合的大小

3.默认集合容量大小

2.ArrayList对象创建

1.无参构造

2.有参构造1

3.有参构造2

3.添加元素add(E e)以及扩容机制

​编辑

后言


前言

源码的剖析有助于理解设计模式,优化编程思想,先叠个甲,我也是学习者,文章不妥处欢迎纠正,一起进步,文章的产出是根据优秀文章,自己研究和理解还有AI的辅助,同时我会以一个学习者(当然我本来就是个学习者)的角度对一些代码抛出自己的疑惑和理解,文章内容持续更新~~~


正文

1.需要了解的一些字段属性

1.存储 ArrayList 元素的数组缓冲区。
transient Object[] elementData;

就是用来存储集合中的元素的数组,他的大小等于集合的容量

以下两个字段属性都是提前预设好的,用于在不同情况下的对elemenData的初始化,不过两个都是空数组

private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
2.集合的大小

这个属性表示当前集合元素的个数也就是elementData中元素的个数

private int size;
3.默认集合容量大小

这个属性是在扩容的时候用的,并不是说无参创建一个对象,初始容量就是10,下面会讲到的。

private static final int DEFAULT_CAPACITY = 10;

2.ArrayList对象创建

1.无参构造

会将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData,此时elementData为空数组

2.有参构造1

有参构造我们可以传入集合的初始容量,如果这个初始容量小于0,就会抛出非法容量的异常,如果等于0,就会把EMPTY_ELEMENTDATA赋值给elementData,也就是空数组,如果大于0,就会将elementData数组初始化成大小为初始化容量的数组

3.有参构造2

先来聊一下这个方法的形参:

Collection<? extends E> c

Collection表示实现了Collection接口,表示c是一个集合,可见,这个构造方法是将一个集合转变成一个新的ArrayList对象,这个E是ArrayList的类型参数,见下下图,我们定义ArrayList对象的时候,要给其指定元素的类型,例如:ArrayList<String> s = new ArrayList<>();?extends E就表示集合c的类型参数需要是E或者是E的子类,这很好理解,我们总不能把一个元素类型是String的集合A转变成元素类型是数字的ArrayList吧,也就是这种写法:

然后我们看方法体:

先调用集合c的toArray方法拿到集合c的Object类型元素集合

Object[] a = c.toArray();

先将数组a的长度赋值给size,然后判断是否为0

(size = a.length) != 0

如果是,就把EMPTY_ELEMENTDATA赋值给elementData,也就是空数组

else {// replace with empty array.elementData = EMPTY_ELEMENTDATA;}

如果不是,会先判断集合c是不是ArrayList类型,如果是,就直接吧集合a赋值给elementDate,如果不是,就会将数组a中的元素拷贝到一个新的Object[]类型的数组,然后赋值给elementDate

if ((size = a.length) != 0) {if (c.getClass() == ArrayList.class) {elementData = a;} else {elementData = Arrays.copyOf(a, size, Object[].class);}}

题外话: 

这里我比较疑惑的一点在于,为什么要判断集合的类型,我去问了一下AI,AI是这么回答的:

其中第一点好理解,第二点就不理解了,他说是为了避免外部集合的修改导致新的ArrayList中的数据跟着出错,为了独立,但经过我的实操,两个集合间的数组是没有引用的,如下图,我创建两个相同类型的集合,让他走有参构造直接赋值的语句,输出结果表明两个集合间的数组是没有引用的,那么如果是类型不一样的集合,数组直接赋值是不是也行?

3.添加元素add(E e)以及扩容机制

先来解释一下modCount,这个字段用于记录集合被修改的次数,准备来说是使集合大小发生变化的修改,我们知道,集合是可以用迭代器进行遍历的,迭代过程中如果我们添加,删除元素等使集合发生大小变化的操作时,会报错,而其判断的依据就是modCount。

为什么不直接使用集合的大小是否变化来判断?

这个很好理解,添加一个元素再删除一个,大小不变

为什么迭代器遍历过程中需要避免直接修改集合?

不过,有些集合框架提供了特定的方法来在迭代过程中安全地修改集合。例如,java.util.ListIterator接口(它是Iterator的子接口,用于遍历List类型的集合)提供了addset方法,可以在遍历List集合(如ArrayListLinkedList等)的过程中安全地添加和修改元素。这些方法会在内部正确地处理集合结构的变化,并且更新迭代器的状态,以确保遍历的一致性和正确性。

 modCount++;

让我们跟进add方法,传入了要添加的元素,elementData数组和元素的个数size

add(e, elementData, size);

add:

size其实也就是存放下一个元素的下标,先判断size是否等于elementData的长度,不等于,就直接在索引处放入e

 elementData[s] = e;

 如果等于,则数组满了,需要扩容

  if (s == elementData.length)elementData = grow();

无论哪种情况,结束后让size+1

size = s + 1;

让我们继续跟进扩容:

grow():

继续跟进: grow(size+1):

这里的形参minCapacity的大小就是size+1,由于此时满容量了才来的,也就是原来的容量+1

int minCapacity

oldCapacity就是原来的容量 

int oldCapacity = elementData.length;

如果oldCapacity>0或者emementData不为空,就通过ArraysSupport.newLength方法计算出新的长度,然后通过Arrays.copyOf方法创建一个新的数组,然后把就数组中的数据复制到新数组中,并将这个新数组赋值给elementData

if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {int newCapacity = ArraysSupport.newLength(oldCapacity,minCapacity - oldCapacity, /* minimum growth */oldCapacity >> 1           /* preferred growth */);return elementData = Arrays.copyOf(elementData, newCapacity);
}

否则,也就是原来的容量是0,此时的minCapacity为1,所以就创建一个容量为DEFAULT_CAPACITY也就是10的数组,终于知道定义的DEFAULT_CAPACITY在哪用了,所以最开始无参构造创建对象时,容量是0,并不是最开始就用了这个DEFAULT_CAPACITY,而是在扩容的时候用

 return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];

让我们跟进ArraysSupport.newLength:

这里的三个形参的大小:oldlength就是原来的集合容量,minGrowth就是minCapacity-prefGrowth,也就是1,preGrowth是oldCapacity>>1,右移一位也就是原来的容量除以2,

int oldLength, int minGrowth, int prefGrowth

首先计算出一个新length----prefLength

int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow

如果这个prefLength大于0并且小于最大容量上限,就返回这个prefLength作为新的容量

if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {return prefLength;}

这个最大容量是int的最大值减8

我们来聊一聊为什么是这个数:

首先,这个数能大于int的最大值吗,不行,有趣的是我们是通过int类型来指定数组的大小的,所有如果超过int,首先出问题的是数据类型那,这是语言层面的限制,并不是说我的电脑内存很大,创建数组完全可以超过这个数,而我们只需要知道这一个就行了(在此之前我搜各种文章,说什么有对象头的存在要占用一部分内存,集合容量大小不能超过这个数),这个容量上限其实是可以超过的,看接下来的代码即可

public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;

如果这个数超过上限,就调用hugeLength方法,传入原来的容量大小和最小增长1

else {// put code cold in a separate methodreturn hugeLength(oldLength, minGrowth);}

hugeLength:

计算出minlength

int minLength = oldLength + minGrowth;

如果这个值小于0,就报错,我们知道,能进入这个方法,说明此时的oldLength已经很大了,已经超过了设置的上限,也就是int的最大值减8,如果小于0,就表示minLength已经超过了int的上限,超出上限后会变成负数,所以报错

 if (minLength < 0) { // overflowthrow new OutOfMemoryError("Required array length " + oldLength + " + " + minGrowth + " is too large");}

如果这个数小于最大上限,就返回最大上限(感觉这一步有点怪~~~)

else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {return SOFT_MAX_ARRAY_LENGTH;}

重点来了!!!否则,返回这个长度,首先,我们要知道,如果能走到判断的最后一步,说明这个数没有超过int上限并且比最大上限要大,那么我们就可以知道,这个最大上限是可以超越的,并不表明集合的最大值不能超过他!!!

 return minLength;

跟进了这么多方法,之后就是方法的回溯,将新的长度一直往上传递,然后添加新的元素,返回true

到此,add方法完毕.


后言

持续更新,如果发现错误欢迎指导和纠正

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

现代密码学|Rabin密码体制及其数学基础 | 椭圆曲线密码体制及其运算 | DH密钥交换及中间人攻击

文章目录 参考Rabin密码体制及其数学基础中国剩余定理二次剩余Rabin密码体制实例 椭圆曲线密码体制及其运算原理运算规则加密解密实例 DH密钥交换及中间人攻击中间人攻击 参考 现代密码学&#xff5c;Rabin密码体制及其数学基础 现代密码学&#xff5c;椭圆曲线密码体制及其运…

硬件选型规则

光源选型: 先用型号中带H的&#xff0c;没有的选标准的. 光源和光源控制器的搭配需要确保接口一致。 根据型号表中的最佳工作距离和相机的尺寸。 光源控制器选型&#xff1a; 首先选择海康风格系列光源控制器考虑与光源的接口匹配。功率应该满足接近光源功率。检查是否退市…

sharedPreference包的使用总结

文章目录 1 概念介绍2 实现方法3 示例代码我们在上一章回中介绍了"如何自定义评分条"相关的内容,本章回中将介绍如何实现本地存储.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 Flutter是一套跨平台的UI框架,它不像原生SDK一样提供本地存储功能,因此,我们在…

TCP连接的时候遇到的异常(目标端口没开放)

import asyncioasync def check_port(ip, port, timeout1):"""检查目标 IP 和端口是否开放:param ip: 目标 IP 地址:param port: 目标端口:param timeout: 超时时间&#xff08;秒&#xff09;"""try:reader, writer await asyncio.open_connec…

C总结(C语言知识点,深化重难点)

C语言 1.使用C语言的7个步骤2.ASCII码3.提高程序可读性的机巧4.如何使用多种整形5.打印多种整形6.课移植类型&#xff1a;stdint.h和inttypes.h7.浮点数常量8.浮点值的上溢和下溢9.使用数据类型11.常量和C预处理器12.转换说明的意义12.1转换不匹配13.副作用和序列点14.数组简介…

burpsuite(6)暴力破解与验证码识别绕过

声明!!! 学习视频来自B站UP主泷羽sec&#xff0c;如涉及侵权马上删除文章 视频链接&#xff1a;泷羽sec-bilibili 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都与本人无关,切莫逾越法律红线,否则后果自负 项目地址&#xff1a;https://github.com/f0ng/cap…

抗DDOS设备

0x00 定义: 抗DDOS设备顾名思义&#xff0c;就是防御DDoS攻击的设备&#xff0c;通常包含三个部分&#xff1a;检测中心、清洗中心和管理中心 检测中心主要负责对流量进行检测&#xff0c;发现流量异常后上报管理中心&#xff0c;由管理中心下发引流策略至清洗中心&#xff0…

systemV信号量与消息队列

目录 引言 ipc简介 ipc在kernel的管理机制&#xff08;简介&#xff09; 信号量 理解信号量 原子 结论 mmap 消息队列 接口 引言 在复杂的软件系统中&#xff0c;进程间的协调和通信是确保系统高效、稳定运行的关键。System V是一套历史悠久且功能强大的进程间通信&a…

【CKS最新模拟真题】Falco 的运行时安全性

系列文章目录 【CKS最新模拟真题】获取多个集群的上下文名称并保存到指定文件中 文章目录 系列文章目录参考地址一、TASK二、解题过程1、问题一解题2、问题二解题 参考地址 CKS考试允许打开falco的地址 https://falco.org/docs/reference/rules/supported-fields/ 一、TASK …

Altium Designer学习笔记 32 DRC检查_丝印调整

基于Altium Designer 23学习版&#xff0c;四层板智能小车PCB 更多AD学习笔记&#xff1a;Altium Designer学习笔记 1-5 工程创建_元件库创建Altium Designer学习笔记 6-10 异性元件库创建_原理图绘制Altium Designer学习笔记 11-15 原理图的封装 编译 检查 _PCB封装库的创建Al…

【原生js案例】webApp实现鼠标移入移出相册放大缩小动画

图片相册这种动画效果也很常见&#xff0c;在我们的网站上。鼠标滑入放大图片&#xff0c;滑出就恢复原来的大小。现在我们使用运动定时器来实现这种滑动效果。 感兴趣的可以关注下我的系列课程【webApp之h5端实战】&#xff0c;里面有大量的css3动画效果制作原生知识分析&…

MetaGPT 安装

1. 创建环境 conda create -n metagpt python3.10 && conda activate metagpt2. 可编辑方式安装 git clone --depth 1 https://github.com/geekan/MetaGPT.git cd MetaGPT pip install -e .3. 配置 metagpt --init-config运行命令&#xff0c;在C盘位置C:\Users\325…

流量地球(Java Python JS C++ C )

题目描述 流浪地球计划在赤道上均匀部署了N个转向发动机,按位置顺序编号为0~N-1。 初始状态下所有的发动机都是未启动状态;发动机启动的方式分为”手动启动"和”关联启动"两种方式;如果在时刻1一个发动机被启动,下一个时刻2与之相邻的两个发动机就会被”关联启动”…

Milvus向量数据库04-Pipelines搭建RAG应用

Milvus向量数据库04-Pipelines搭建RAG应用 Zilliz Cloud Pipelines 可以将文档、文本片段和图像等非结构化数据转换成可搜索的向量并存储在 Collection 中。本文将介绍 Zilliz Cloud Pipelines 的三种主要类型并提供示例代码&#xff0c;展示如何使用 Pipelines 搭建 RAG 应用。…

离线写博客(失败) - 用Markdown来离线写博客

因为想控制一下用网&#xff0c;但是又有写博客的需求&#xff0c;所以想研究一下离线写博客。 我看CSDN上面好像有很多介绍&#xff0c;Windows Live Writer 啦&#xff0c;Markdown啦&#xff0c;还有一些其他的&#xff0c;我看了一下&#xff0c;好像 Markdown还有点儿靠谱…

【洛谷】B3844 [GESP样题 二级] 画正方形(详细注释)

#include <iostream> using namespace std; int main() {//声明一个整型变量n&#xff0c;用于接收输入的数值&#xff0c;该数值将决定后续输出图案的行数和列数int n; cin >> n;//声明两个整型变量i和j&#xff0c;分别用作外层循环和内层循环的计数器int i, j;/…

Linux-音频应用编程

ALPHA I.MX6U 开发板支持音频&#xff0c;板上搭载了音频编解码芯片 WM8960&#xff0c;支持播放以及录音功能&#xff01;本章我们来学习 Linux 下的音频应用编程&#xff0c;音频应用编程相比于前面几个章节所介绍的内容、其难度有所上升&#xff0c;但是笔者仅向大家介绍 Li…

番茄社区双端视频APP源码_内附安装教程

新版视频源码|类似番茄APP视频付费软件APP源码教程 番茄社区双端视频APP源码&#xff0c;带安装教程&#xff0c;源码非组件&#xff0c;短视频、图片、交流、讨论、电影、电视剧。 没有什么问题宝塔就可以搭建&#xff0c;采集方面是火车头可以自己写规则&#xff01;

Onchain 正在蚕食 Offchain

目录 未来在链上 价值创造 技术加速 机构采用 小队&#xff1a;加速链上经济 我们正在进入一个代码就是货币、信任被编程、全球接入打破传统经济界限的时代。这种新兴模式就是我们所说的链上经济。 在此领域&#xff0c;Solana 因其在基础设施和应用程序方面的持续创新而脱颖而…

【LeetCode】80.删除有序数组中的重复项II

题目链接&#xff1a; 80.删除有序数组中的重复项II 题目描述&#xff1a; 解题思路&#xff1a; 按照题目中要求&#xff0c;必须在原来数组中进行修改&#xff0c;并且在O(1)额外空间条件下完成。因此我们可以使用双指针算法&#xff0c;算法具体流程如下&#xff1a; 如…