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.mzph.cn/bicheng/63403.shtml

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

相关文章

重磅更新:CnosDB 2.3.5.4 版本上线, 性能提升,问题修复一网打尽

&#x1f4e2; 重磅更新&#xff1a;CnosDB 2.3.5.4 版本上线, 性能提升&#xff0c;问题修复一网打尽 &#x1f4e2; 我们很高兴地向大家介绍最新版本的更新&#xff0c;以下是本次更新的主要内容&#xff1a; &#x1f539; 版本号&#xff1a;2.3.5.4 &#x1f539; 发布…

SD-WAN 2.0 在金融行业的典型应用场景

目录 全扁平化组网 场景需求 应用方案 SD-WAN 2.0 在金融行业的创新实践 SD-WAN5G提高金融行业网络接入可靠性 全扁平化组网 随着金融机构数字化转型的推进&#xff0c;机构业务的多样性、复杂性、 个性化等要求&#xff0c;对现有的金融机构网络架构与网管人员运维模式提出…

如何延长相机电池续航时间

如果你曾在拍摄过程中突然发现相机电池电量不足&#xff0c;就会知道那有多让人紧张和沮丧了。无论你是在拍摄小朋友的生日派对、家庭聚会&#xff0c;还是作为一名专业摄影师在工作&#xff0c;保持电池有电都是至关重要的。否则&#xff0c;你就有可能错过精彩瞬间&#xff0…

C#开发-集合使用和技巧(十)Union用法-并集

在 C# 中&#xff0c;IEnumerable 的 Union 方法用于返回两个序列的并集。Union 方法会去除重复的元素&#xff0c;确保结果集中每个元素都是唯一的。以下是 Union 方法的基本用法&#xff1a; 基本语法 public static IEnumerable<TSource> Union<TSource>(this…

轻量化特征融合 | YOLOv11 引入一种基于增强层间特征相关性的轻量级特征融合网络 | 北理工新作

本改进已同步到Magic框架 摘要—无人机图像中的小目标检测由于分辨率低和背景融合等因素具有挑战性,导致特征信息有限。多尺度特征融合可以通过捕获不同尺度的信息来增强检测,但传统策略效果不佳。简单的连接或加法操作无法充分利用多尺度融合的优势,导致特征之间的相关性不…

ABAP 系统变量SY-INDEX与SY-TABIX的区别

ABAP系统变量SY-INDEX与SY-TABIX都是在循环中使用&#xff1a; SY-INDEX在Do...EndDo和While...EndWhile中起作用&#xff1b; SY-TABIX在Loop...EndLoop中有效。 详见如下实例&#xff1a; REPORT ztest_index_tabix.DATA:lit_vbak TYPE STANDARD TABLE OF vbak,lwa_vbak …

方案拆解 | 打击矩阵新规频出!2025矩阵营销该怎么玩?

社媒平台的矩阵营销又要“变天”了&#xff1f;&#xff01; 11月18日&#xff0c;小红书官方发表了被安全薯 称为“小红书史上最严打击黑灰产专项”新规&#xff0c;其中就包括黑灰产矩阵号的公告。 ▲ 图源&#xff1a;小红书 实际上&#xff0c;不包括这次&#xff0c;今年…

Lua语言入门 - Lua 数组

Lua 数组 数组&#xff0c;就是相同数据类型的元素按一定顺序排列的集合&#xff0c;可以是一维数组和多维数组。 在 Lua 中&#xff0c;数组不是一种特定的数据类型&#xff0c;而是一种用来存储一组值的数据结构。 实际上&#xff0c;Lua 中并没有专门的数组类型&#xff…

SVM的基本思想

一、SVM的基本思想 SVM的基本思想是在样本的向量空间中寻找一个超平面&#xff0c;使得两类样本被分割在平面的两端。这样的平面理论上有无穷多个&#xff0c;但SVM的目标是找到一个最优的超平面&#xff0c;即两侧距离超平面最近的样本点到超平面的距离被最大化的超平面。这个…

Java基于SpringBoot的网上订餐系统,附源码

博主介绍&#xff1a;✌Java老徐、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…

【 工具变量】IPCC碳排放因子数据测算表

一、数据简介&#xff1a; 排放因子法是IPCC提出的一种碳排放估算方法&#xff0c;也是目前适用范围最广、应用最为普遍的方法。将各类能源消耗的实物统计量转变为标准统计量&#xff0c;再乘以各自的碳排放因子&#xff0c;加总之后就可以得到碳排放总量。如果按照ISO14064标…

备忘录模式的理解和实践

引言 在软件开发中&#xff0c;我们经常会遇到需要保存对象状态并在某个时间点恢复到该状态的需求。这种需求类似于我们平时说的“后悔药”&#xff0c;即允许用户撤销之前的操作&#xff0c;恢复到某个之前的状态。备忘录模式&#xff08;Memento Pattern&#xff09;正是为了…

湖南铂乐家具新潮流,岛台不再是大平层的专属

湖南铂乐家具设计师们以巧思打破常规&#xff0c;无论是精致温馨的小户型公寓&#xff0c;还是布局紧凑的普通住宅&#xff0c;都能找到适配的岛台设计。以往岛台总是与宽敞开阔的大平层空间紧密相连&#xff0c;仿佛是大户型的身份象征。而如今岛台不再是大平层的专属。 在固…

RK3568笔记3:开发板启动流程

第1章 启动流程 1.1 上电复位 CPU 复位&#xff0c;进入启动模式。系统硬件查找启动设备&#xff08;如 eMMC&#xff09;。 1.2 ROM Code 阶段&#xff08;硬件引导&#xff09; 在片上 ROM 中存储的启动代码&#xff08;BootROM&#xff09;运行。ROM Code 从 eMMC 的 Boo…

重邮+数字信号处理实验三:z变换及离散LTI系统的z域分析

实验目的&#xff1a; &#xff08; 1 &#xff09;学会运用 Matlab 求离散时间信号的有理函数 z 变换的部分分式展开&#xff1b; &#xff08; 2 &#xff09;学会运用 Matlab 分析离散时间系统的系统函数的零极点&#xff1b; &#xff08; 3 &#xff09;学会运用 …

dolphinScheduler 任务调度

#Using docker-compose to Start Server #下载&#xff1a;https://dlcdn.apache.org/dolphinscheduler/3.1.9/apache-dolphinscheduler-3.1.9-src.tar.gz $ DOLPHINSCHEDULER_VERSION3.1.9 $ tar -zxf apache-dolphinscheduler-"${DOLPHINSCHEDULER_VERSION}"-src.t…

node.js中跨域请求有几种实现方法

默认情况下&#xff0c;出于安全考虑&#xff0c;浏览器会实施同源策略&#xff0c;阻止网页向不同源的服务器发送请求或接收来自不同源的响应。 同源策略&#xff1a;协议、域名、端口三者必须保持一致 <!DOCTYPE html> <html lang"en"> <head>&l…

【机器学习】机器学习的基本分类-监督学习-决策树(Decision Tree)

决策树是一种树形结构的机器学习模型&#xff0c;适用于分类和回归任务。它通过一系列基于特征的条件判断来将数据分割为多个子区域&#xff0c;从而预测目标变量的值。 1. 决策树的结构 根节点&#xff08;Root Node&#xff09; 决策树的起点&#xff0c;包含所有样本。根据某…

支付宝租赁小程序助力便捷生活新方式

内容概要 支付宝租赁小程序为现代人带来了许多惊喜&#xff0c;它不仅仅是一个简单的租赁平台&#xff0c;更是生活中不可或缺的好帮手。想象一下&#xff0c;无论你缺少什么&#xff0c;从工具到家居用品&#xff0c;只需轻轻一点&#xff0c;便能轻松找到需要的物品。这个小…

搭建高可用负载均衡系统:Nginx 与云服务的最佳实践

搭建高可用负载均衡系统&#xff1a;Nginx 与云服务的最佳实践 引言 在项目开发过程中&#xff0c;我们通常在开发和测试阶段采用单机架构进行开发和测试。这是因为在这个阶段&#xff0c;系统的主要目的是功能实现和验证&#xff0c;单机架构足以满足开发人员的日常需求&…