JAVASE进阶:Collection高级(2)——源码剖析ArrayList、LinkedList、迭代器

news2024/11/18 17:42:44

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:JAVASE进阶:Collection高级(1)——源码分析contains方法、lambda遍历集合
📚订阅专栏:JAVASE进阶
希望文章对你们有所帮助

ArrayList的底层其实还是比较复杂的,如果你去尝试阅读源码的话,但是这又是面试常考的问题,网上有些面经会说当创建ArrayList的时候会在底层创建长度为10的数组,后续会以1.5倍扩容,但是实际上这既不完整,也不准确。因为ArrayList除了用add方法添加元素,还有用addAll方法添加元素。
在这里剖析一下ArrayList,另外剖析一下LinkedList和迭代器的底层。

源码剖析ArrayList、LinkedList

  • ArrayList
    • 原理
    • 源码剖析ArrayList
  • LinkedList
    • LinkedList方法
    • 源码剖析LinkedList
  • 源码剖析迭代器

ArrayList

原理

新手还是先大致讲解一下原理有个印象再去看源码会比较好:

1、利用空参创建的集合,在底层创建一个默认长度为0的数组elementData,并用size=0记录元素个数/元素下次存入位置
2、添加第一个元素时,底层会创建一个新的长度为10的数组,把元素添加进去,size=1
3、存满时,将会扩容1.5倍(其实就是直接再创建一个长度15的数组并把之前数组的元素拷贝进来)
4、若一次添加了多个元素,扩容1.5倍还是放不下,则创建新数组的长度以实际为准

注意点:

1、创建ArrayList的时候底层默认长度为0,添加第一个元素时才会创建出长度为10的数组
2、不是每次存满都扩容1.5倍,因为ArrayList添加元素的方法还有addAll,添加元素过于多就要以实际为准

源码剖析ArrayList

1、Ctrl+N输入ArrayList:
在这里插入图片描述
2、进入后就是ArrayList的空参构造,定位一下DEFAULTCAPACITY_EMPTY_ELEMENTDATA可以发现其一开始创建的确实是空集合,并且size初始值为0:
在这里插入图片描述
在这里插入图片描述
3、Alt+7找到这个类下面的所有成员变量、方法:
在这里插入图片描述
4、右边栏点击add方法,可以看到它又调用了另一个add方法,传入3个参数(当前要添加的元素、集合底层的数组名、集合长度/当前元素应存入的位置):
在这里插入图片描述
5、跟踪这个add方法,可以看到,在添加元素之前会先判断这个容器满了没有,如果满了就会调用grow()函数扩容,然后再增加元素并更新size。
在这里插入图片描述
6、跟踪grow()方法,它会将size+1传入调用另一个grow做扩容,所以当添加了第一个元素的时候,方法体就是return grow(1)
在这里插入图片描述
7、接着跟踪grow,可以看到函数体先记录了原来数组的老容量,然后进行逻辑判断,若是第一次添加元素,老容量还是0,因此会走else:
先比较了DEFAULT_CAPACITY(10)和minCapacity谁更大,若minCapacity更大则以此为标准,但是由于这里是用add方法而不是addAll方法批量添加,在这里显然是创建了长度为10的数组:
在这里插入图片描述
8、下一次走到该扩容函数的时候,长度是已经大于10了,参数符合if条件,此时oldCapacity=10,minCapacity=11,进入if条件,调用了newLength方法去计算新容量的大小,传入的参数为(老容量,最小扩容长度、老容量/2)
在这里插入图片描述
9、跟踪进入newLength方法,minGrowth和prefGrowth可以视为至少要新增的容量大小默认新增容量大小,取最大值后加上oldLegth,默认的情况即为老容量 + 老容量/2 = 1.5 * 老容量,若minGrowth更大,说明默认的1.5倍扩容还是不够大,这时候容量大小就需要以实际长度为准了,这种情况会出现在ArrayList的addAll函数中:
在这里插入图片描述
10、若不满足if情况,则说明此时超过了限定的大小,就需要用hugeLength函数进行处理,跟踪查看该函数,基本上只会执行else if,返回最大限定值:
在这里插入图片描述
至此,底层源码剖析完毕,其实这部分的原理跟之前的StringBuilder有点像,只不过StringBuilder一创建的默认长度就是16,扩容的时候是以原容量 * 2 + 2的方式进行的,也同时拥有grow()方法中的预防机制即插入元素数量过多,导致长度超过扩容后大小,则容量以实际长度为准,所以阅读源码起来感觉很熟悉。

LinkedList

LinkedList方法

LinkedList底层数据结构是双向链表,查询慢但是增删快,操作首尾元素的速度极快。
每一个结点都有3个部分:前一个结点地址值、当前结点的value、后一个结点的地址值。LinkedList本身就多了很多直接操作首尾元素的特有API。

函数名
public void addFirst(E e)
public void addLast(E e)
public E getFirst()
public E getLast()
public E removeFirst()
public E removeLast()

这些方法全部都是针对首尾的,但是List类都是具有根据索引添加、修改和删除元素的操作的,上面的一些操作相对使用的会比较少。

源码剖析LinkedList

1、Ctrl+N选中LinkedList函数,进入:
在这里插入图片描述
2、查看LinkedList中的内部类Node,这就表示了链表中的每个结点:
在这里插入图片描述
3、找到add方法,其调用了addLast函数,前面讲过addLast是专门封装的给LinkedList操作尾部的API:
在这里插入图片描述
4、跟踪LinkedList,可以看出,LinkedList除了插入元素的时候需要把之前末尾结点中的next赋值新增结点,把新增结点的pre赋值为前一个尾结点,还单独记录了头结点和尾结点的位置,其实就是头插法+尾插法
在这里插入图片描述

源码剖析迭代器

一般是这么使用迭代器的:

ArrayList<String> list = new ArrayList<>();
Iterator<String> it = new list.Iterator();
while(it.hasNext()){
	String str = it.next();
	System.out.println(str);
}

接下来跟踪器源码:
1、跟踪Iterator方法,其底层是创建了迭代器对象(Itr)并返回:
在这里插入图片描述
2、跟踪进入Itr,其实它就是一个ArrayList的内部类而已(我们创建的迭代器就是ArrayList对象的迭代器):
(1)cursor表示游标,也就是迭代器中的指针,空参构造默认初始化为0,因此迭代器对象创建后,指针默认指向0索引
(2)lastRet表示之前操作的索引的位置
(3)expectedModCount与并发修改异常有关系
(4)hasNext判断是否有下一个元素(迭代完了没有)
(5)next会获取当前元素并且将指针移动一次
(6)checkForComodification是一种安全性检测
在这里插入图片描述
3、跟踪进入checkForComodification方法,其中modCount变量,这个变量的意思是集合变化的次数,因为删除、修改和添加元素都会使得modCount+1。
这个函数体中先判断当前集合中最新的变化次数和一开始记录的变化次数是否相同,不相同就说明出现了并发修改,此时会报并发修改异常。
在这里插入图片描述
因此,无论我们使用迭代器、增强for还是lambda表达式(底层遍历方式都是迭代器),遍历集合的过程中,不要去使用集合中的方法去增加或删除元素。

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

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

相关文章

IP地址信息在保险行业的创新应用与解决方案

随着数字化时代的来临&#xff0c;保险行业正积极探索新的技术手段&#xff0c;以提升服务效能、降低风险&#xff0c;并更好地满足客户需求。IP地址信息作为一种重要的数字化工具&#xff0c;在保险行业中展现了广泛的应用前景。IP数据云将深入探讨IP地址信息在保险行业中的创…

【极数系列】Flink集成KafkaSource 实时消费数据(10)

文章目录 01 引言02 连接器依赖2.1 kafka连接器依赖2.2 base基础依赖 03 连接器使用方法04 消息订阅4.1 主题订阅4.2 正则表达式订阅4.3 Partition 列分区订阅 05 消息解析06 起始消费位点07 有界 / 无界模式7.1 流式7.2 批式 08 其他属性8.1 KafkaSource 配置项&#xff08;1&…

Stable Diffusion 模型下载:RealCartoon3D - V14

文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十 下载地址 模型介绍 RealCartoon3D 是一个动漫卡通混合现实风格的模型&#xff0c;具有真实卡通的 3D 效果&#xff0c;当前更新到 V14 版本。 RealCartoon3D 是我上传的第一个模型。…

一文掌握SpringBoot注解之@Configuration知识文集(5)

&#x1f3c6;作者简介&#xff0c;普修罗双战士&#xff0c;一直追求不断学习和成长&#xff0c;在技术的道路上持续探索和实践。 &#x1f3c6;多年互联网行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责人。 &#x1f389;欢迎 &#x1f44d;点赞✍评论…

C++后端开发之Sylar学习三:VSCode连接Ubuntu配置Gitee

C后端开发之Sylar学习三&#xff1a;VSCode连接Ubuntu配置Gitee 为了记录学习的过程&#xff0c;学习Sylar时写的代码统一提交到Gitee仓库中。 Ubuntu配置Gitee 安装git sudo apt-get install -y git配置用户名和邮箱 git config --global user.name 用户名 …

计算机项目SpringBoot项目 办公小程序开发

从零构建后端项目、利用UNI-APP创建移动端项目 实现注册与登陆、人脸考勤签到、实现系统通知模块 实现会议管理功能、完成在线视频会议功能、 发布Emos在线办公系统 项目分享&#xff1a; SpringBoot项目 办公小程序开发https://pan.baidu.com/s/1sYPLOAMtaopJCFHAWDa2xQ?…

第四讲 混合背包问题

【题意分析】 这道题转换一下即可&#xff0c;将题中出现的0/1背包问题和完全背包问题转换为多重背包问题即可&#xff1a; if(s -1) s 1; else if(!s) s V/v;【参考文献】 第三讲 多重背包问题②——二进制优化 完成这个转换之后&#xff0c;再使用二进制优化即可完成&a…

Java 学习和实践笔记(1)

2024年&#xff0c;决定好好学习计算机语言Java. B站上选了这个课程&#xff1a;【整整300集】浙大大佬160小时讲完的Java教程&#xff08;学习路线Java笔记&#xff09;零基础&#xff0c;就从今天开始学吧。 在这些语言中&#xff0c;C语言是最基础的语言&#xff0c;绝大多…

PYthon进阶--网页采集器(基于百度搜索的Python3爬虫程序)

简介&#xff1a;基于百度搜索引擎的PYthon3爬虫程序的网页采集器&#xff0c;小白和爬虫学习者都可以学会。运行爬虫程序&#xff0c;输入关键词&#xff0c;即可将所搜出来的网页内容保存在本地。 知识点&#xff1a;requests模块的get方法 一、此处需要安装第三方库reques…

华为OD机试真题C卷-篇3

文章目录 查找一个有向网络的头节点和尾节点幼儿园篮球游戏 查找一个有向网络的头节点和尾节点 在一个有向图中&#xff0c;有向边用两个整数表示&#xff0c;第一个整数表示起始节点&#xff0c;第二个整数表示终止节点&#xff1b;图中只有一个头节点&#xff0c;一个或者多…

K8S之标签的介绍和使用

标签 标签定义标签实操1、对Node节点打标签2、对Pod资源打标签查看资源标签删除资源标签 标签定义 标签就是一对 key/value &#xff0c;被关联到对象上。 标签的使用让我们能够表示出对象的特点&#xff0c;比如使用在Pod上&#xff0c;能一眼看出这个Pod是干什么的。也可以用…

Flink cdc3.0动态变更表结构——源码解析

文章目录 前言源码解析1. 接收schema变更事件2. 发起schema变更请求3. schema变更请求具体处理4. 广播刷新事件并阻塞5. 处理FlushEvent6. 修改sink端schema 结尾 前言 上一篇Flink cdc3.0同步实例 介绍了最新的一些功能和问题&#xff0c;本篇来看下新功能之一的动态变更表结…

【华为】GRE Over IPsec 实验配置

【华为】GRE Over IPsec 实验配置 前言报文格式 实验需求配置拓扑GRE配置步骤IPsec 配置步骤R1基础配置GRE 配置IPsec 配置 ISP_R2基础配置 R3基础配置GRE 配置IPsec 配置 PCPC1PC2 抓包检查OSPF建立GRE隧道建立IPsec 隧道建立Ping 配置文档 前言 GRE over IPSec可利用GRE和IP…

什么是MVVM模型

MVVM&#xff08;Model-View-ViewModel&#xff09;是一种用于构建 Web 前端应用程序的架构模式。它是从传统的 MVC&#xff08;Model-View-Controller&#xff09;模型演变而来&#xff0c;旨在解决界面逻辑与业务逻辑之间的耦合问题。 在传统的 MVC 架构中&#xff0c;View …

SQL 表信息 | 统计 | 脚本

介绍 统计多个 SQL Server 实例上多个数据库的表大小、最后修改时间和行数&#xff0c;可以使用以下的 SQL 查询来获取这些信息。 脚本 示例脚本&#xff1a; DECLARE Query NVARCHAR(MAX)-- 创建一个临时表用于存储结果 CREATE TABLE #TableSizes (DatabaseName NVARCHAR…

小白水平理解面试经典题目LeetCode 20. Valid Parentheses【栈】

20.有效括号 小白渣翻译 给定一个仅包含字符 ‘(’ 、 ‘)’ 、 ‘{’ 、 ‘}’ 、 ‘[’ 和 ‘]’ &#xff0c;判断输入字符串是否有效。 输入字符串在以下情况下有效&#xff1a; 左括号必须由相同类型的括号封闭。 左括号必须按正确的顺序关闭。 每个右括号都有一个对…

error getting ip from ipam: operation get is not supported on blockkey

无论是否通过注释指定ip&#xff0c;都不支持cni Claim操作。 查了好久。发现是版本问题&#xff0c;我的calico版本太老了。是3.5的calico &#xff0c;使用 kubernetes 数据存储时&#xff0c;不支持 Calico IPAM。 需要更新calico到3.6以上&#xff0c;支持 kubernetes 数…

【QT】opcuaServer 的构建

【QT】opcuaServer 的构建 前言opcuaServer实现测试 前言 在博文【opcua】从编译文件到客户端的收发、断连、节点查询等实现 中&#xff0c;我们已经介绍了如何在QT 中创建opucaClient 。在本期的博文中&#xff0c;我们基于之前的部署环境&#xff0c;介绍一下如何构建opcuaS…

【DDD】学习笔记-数据实现模型

SQL 与存储过程 倘若选择关系型数据库&#xff0c;组成数据实现模型的主力军是 SQL 语句&#xff0c;这是我们不得不面对的现实。毕竟&#xff0c;针对数据建模的实现者大多数担任 DBA 角色&#xff0c;他&#xff08;她&#xff09;们掌握的操作数据的利器就是 SQL。正如前面…

【算法与数据结构】583、72、LeetCode两个字符串的删除操作+编辑距离

文章目录 一、583、两个字符串的删除操作二、72、编辑距离三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、583、两个字符串的删除操作 思路分析&#xff1a;本题的思路和115、不同的子序列差不多&#xff0c;只是变成…