java LinkedList 源码分析(通俗易懂)
目录
一、前言
二、LinkedList类简介
三、LinkedList类的底层实现
四、LinkedList类的源码解读
1.add方法解读 :
〇准备工作 。
①跳入无参构造。
②跳入add方法。
③跳入linkList方法。
④增加第一个元素成功。
⑤向链表中添加第二个元素。
2.remove方法解读 :
〇准备工作 。
①关于LinkedList类的remove方法。
②跳入remove() 方法。
③跳入removeFirst() 方法。
④跳入unlinkFirst() 方法。
⑤第一个元素被除去。
五、总结
一、前言
大家好,今天给大家带来的是LinkedList类的内容分享。对于单列集合List的三个最常用的实现类——ArrayList, Vector, LinkedList,在前面的小节中,我们已经分析过了ArrayList类和Vector类的源码。但对于List接口的第三大实现类LinkedList,由于其底层涉及了较多数据结构的知识,而本篇博文属于《java基础》专栏,因此主要面向基础阶段,因此up准备简单给大家过一下就好,不会讲太深,大家可放心食用,更多内容up准备在将来的数据结构专栏再深度讲解。
注意 : ① 代码中的注释也很重要; ② 不要眼高手低,自己跟着过一遍才能知道怎么用。 ③ 点击侧边栏目录或者文章开头的目录可以跳转。良工不示人以朴,所有文章都会适时改进。大家如果有什么问题,都可以在评论区一块儿交流,或者私信up。 感谢阅读!
二、LinkedList类简介
1.LinkedList是一种常见的线性表,每一个结点中都存放了下一个结点的地址。LinkedList类属于java.base模块,java.util包下,如下图所示 :

我们再来看看LinkedList 类的类图,如下所示 :

2.链表又分为单向链表和双向链表。一个单向链表包含两个值——当前结点的值和下一个结点的地址值;一个双向链表包含三个值——前一个结点的地址值,当前结点的值和下一个结点的地址值。
3.LinkedList底层实现了双向链表和双端队列的特点。
4.同ArrayList类似,可以向LinkedList集合中添加任意元素,包括null,并且元素可以重复。
5.同ArrayList一样,LinkedList也没有实现线程同步,因此线程不安全。
三、LinkedList类的底层实现
1.LinkedList的底层维护了一个双向链表。在IDEA的类图中,我们查看LinkedList类的字段可以发现,LinkedList类中维护了两个属性first和last,见名知意,它们分别指向双向链表的首结点和尾结点。
2.每个节点(Node对象)中又维护了prev,next,item,三个属性,其中通过prev指向前一个结点,通过next指向后一个结点,从而实现双向链表。
3.LinkedList中元素的添加和删除,在底层不是通过数组来完成的,而是通过链表来完成的,因此LinkedList相对来说效率更高。
PS :
以上内容涉及到了数据结构基础中关于链表的部分知识(比如什么是首结点和尾结点),当然,仅仅是涉及到一些基础的概念性的知识。
双向链表的示意图如下 :

这里up用java来模拟一个简单的双向链表,现在我们想创建三个璃月人对象——刻晴,甘雨,钟离,并且让它们形成如下的双向链表关系:

以Link_Simulation类为例,代码如下 :
package csdn.knowledge.api_tools.gather.list;import java.util.LinkedList;/*** @author : Cyan_RA9* @version : 21.0*/
public class Link_Simulation {public static void main(String[] args) {//演示 : 用java模拟一个简单的双向链表。//创建璃月人对象Node keqing = new Node("刻晴");Node ganyu = new Node("甘雨");Node zhongli = new Node("钟离");//完成双向链表keqing.next = ganyu;ganyu.next = zhongli;zhongli.pre = ganyu;ganyu.pre = keqing;Node first = keqing;Node last = zhongli;//遍历链表(头 ——> 尾)while (true) {if (first == null) {break;}System.out.println(first); //输出当前对象的信息first = first.next; //更改指向}System.out.println("=====================================");//遍历链表(尾 ——> 头)while (true) {if (last == null) {break;}System.out.println(last); //输出当前对象的信息last = last.pre; //更改指向}}
}class Node {public Object item; //存放当前结点的数据。public Node pre; //指向前一个结点public Node next; //指向后一个结点public Node(Object name) {this.item = name;}public String toString() {return "Node 's name = " + item;}
}
运行结果 :

四、LinkedList类的源码解读
1.add方法解读 :
〇准备工作 。
up以LinkedList_Demo1为演示类,代码如下 :(在创建对象那行代码设置断点)
package csdn.knowledge.api_tools.gather.list;import java.util.LinkedList;/*** @author : Cyan_RA9* @version : 21.0*/
public class LinkedList_Demo1 {public static void main(String[] args) {LinkedList linkedList = new LinkedList();linkedList.add(11);linkedList.add(141);System.out.println(linkedList);}
}
①跳入无参构造。
如下图所示 :

可以看到,LinkedList类的无参构造其实是什么也没有做,我们跳出无参构造。无参构造器执行完毕后,我们可以看到LinkedList对象已经初始化完毕,如下图所示 :

注意看,此时 first 和 last 均为null类型。所以,链表此时是这样一个效果 :

②跳入add方法。
如下图所示 :

因为我们要向链表中添加的元素为int类型,所以第一次跳入add方法是一个自动装箱的过程,我们不用管他,直接跳出。
再次跳入add方法,如下图所示 :

③跳入linkList方法。
形参列表的"E e"表示我们当前要添加的元素,所以此时e = 11。add方法中调用了linkLast方法,不用想也能猜到,这个linkLast方法里面完成了添加元素的操作,我们继续追进去看看,如下图所示 :

我们一步一步来看——
首先, Node是“结点”的意思。
其次,还记得我们上面提到说——first 和 last此时均为null。所以,linkLast方法内,第一步是定义了一个Node类型的常指针l,并为其赋初值为last(即null);
接着,又定义了一个Node类型的常量newNode,见名知意,"newNode"就是我们要添加的新结点。那么,为newNode初始化的这个带参构造是怎么执行的呢?这三个实参分别是干嘛的?别急,我们这就通过Ctrl + b/B快捷键,追到其源码中看看,如下图所示 :

一看源码我们就明白了,这不就是上文中我们提到的——双向链表的三个值吗?所以,对应此处的三个实参,l就是prev,此时为null;e就是已经装箱好的11;null就是next的值。因此,newNode引用此时指向的就是一个前后均为空,值为11的新结点。并且,之后又令last指向了该新结点。

继续向下执行,是一个if-else的复合条件语句。判断条件"l == null"显然满足,令first也指向了该新结点;之后,又令size自增1(size表示当前链表中元素的个数),modCount也自增1(修改次数)。
④增加第一个元素成功。
好的,linkList方法执行完毕后,此时链表就长下面这样子 :

接下来,我们逐层跳出,直到演示类中。我们可以看到此时链表的状态,如下图所示 :

可以看到,first 和 last 都指向了同一个结点,并且该结点中prev和next均为null。
⑤向链表中添加第二个元素。
前面几个步骤都一样,我们就不再赘述了,直接从linkList方法开始说起,如下 :

还是一步一步来看——
首先,令Node类型的常指针l 指向了last所指向的结点(即我们刚刚添加的第一个结点)。
其次,第二个新结点进行初始化工作。注意,第一个实参l 代表的是新结点的prev,而l 此时又指向了第一个结点,因此,这一步实现了——第二个新节点的prev指向了第一个结点。
接着,又令last指向了第二个新结点(此时first仍指向第一个结点)。
然后,就是if-else的判断语句了,因为l 已经指向了第一个结点,不为空,所以执行ele中的语句,令第一个结点的next指向了第二个新结点。
最后,就是size和modCount的自增。
所以,第二次linkList方法执行完毕后,链表就应该长下面这个样子 :

2.remove方法解读 :
〇准备工作 。
up以LinkedList_Demo2为演示类,代码如下 :(在remove方法那行代码设置断点)
package csdn.knowledge.api_tools.gather.list;import java.util.LinkedList;/*** @author : Cyan_RA9* @version : 21.0*/
public class LinkedList_Demo2 {public static void main(String[] args) {LinkedList linkedList = new LinkedList();linkedList.add(11);linkedList.add(141);linkedList.add(5);System.out.println("添加三个元素后,当前链表 = " + linkedList);linkedList.remove();System.out.println("删除第一个元素后,当前链表 = " + linkedList);}
}
运行结果 :

如代码所示,我们事先在链表中加入三个元素:11,141,5。则在删除元素之前,我们的双向链表应该长下面这样子 :

①关于LinkedList类的remove方法。
通过查看API文档,我们可得知LinkedList类的remove有三个重载方法,如下图所示 :

其中,形参列表为空的remove() 方法,其内部默认调用的是removeFrist方法,即默认删去链表中的第一个元素;形参列表需要传入一个索引的remove(int index) 方法,可以删去链表中指定索引位置的元素,较复杂;形参列表需要传入一个Object类型的值的remove(Object o)方法,是删去链表中与该值匹配的第一个元素。
up就以最简单的remove() 方法,通过Debug,来给大家分析一下。
②跳入remove() 方法。
我们直接在remove方法的调用行设置断点跳过去,并跳入remove方法,如下图所示 :

③跳入removeFirst() 方法。
可以看到,果然是调用了removeFirst() 方法,那它底层到底是如何把链表的第一个元素给干掉的?我们继续往里面追,如下图所示 :

removeFirst方法内部还是比较简洁的。首先,它令一个Node类型的常指针f 指向了首结点(即第一个存放有效数据的结点),然后判断头结点是否为空。由于我们一开始就在链表中添加了3个元素,所以此处f 肯定不为空。因此,if语句中的内容会跳过,return一个unlinkFirst() 方法的返回值。
④跳入unlinkFirst() 方法。
可以看到,removeFirst方法内部并没有执行删除操作的代码。我们继续追入unlinkFirst方法,如下图所示 :

哎呀我趣,看这一大串,显然删除操作是在这里面完成的。
老规矩,我们一步一步来看——
首先,第一条语句不用看。因为这只是在函数最后return的删除掉的元素的值,与删除过程本身无关。
其次,第二条语句,它令一个Node类型的常指针next指向了第一个结点的next属性所指向的值,即指向了第二个结点,如下图所示 :

接着,又依次置空了第一个结点的item和next,并且令first 指向了第二个结点,如下图所示 :

继续, 由于next现在指向的是第二个结点,不为空,所以接下里要进入else语句中。else语句中,令第二个结点的prev为null,如下图所示 :

⑤第一个元素被除去。
至此,第一个元素已经被干掉了。回忆一下案发现场,jvm先是破掉了第一个结点的"盾牌"(即first);接着又切断了第一个结点的"逃跑路线"(即next),最后又斩断了第一个结点的"后援"(即第二个结点的prev)。那第一个结点手无寸铁,又成了单兵作战,留给它的命运只能是被gc垃圾回收器除去,哎,可悲。(bushi)
五、总结
🆗,以上就是关于LinkedList源码分析的全部内容了。由于《java基础》是面向基础阶段系列博文,而LinkedList作为链表,已经属于数据结构的内容了。因此本篇博文中up也是尽量"避重就轻",已不至于过于晦涩。当然,对于有些在学C语言的时候接触过链表的小伙伴儿们来说,应该还是没什么难度的。好的,至此我们的单列集合之List接口就算是全部搞定了。下一小节我们将开始进入set集合的内容,大家不见不散😆。感谢阅读!
System.out.println("END----------------------------------------------------------");
相关文章:
java LinkedList 源码分析(通俗易懂)
目录 一、前言 二、LinkedList类简介 三、LinkedList类的底层实现 四、LinkedList类的源码解读 1.add方法解读 : 〇准备工作 。 ①跳入无参构造。 ②跳入add方法。 ③跳入linkList方法。 ④增加第一个元素成功。 ⑤向链表中添加第二个元素。 2.remove方法解读 : 〇准备工…...
Vue中实现路由跳转的三种方式详细分解
vue中实现路由跳转的三种方式 目录 vue中实现路由跳转的三种方式 一、使用vue-router 1.下载vue-router模块到当前工程 2.在main.js中引入VueRouter函数 3.添加到Vue.use()身上 – 注册全局RouterLink和RouterView组件 4.创建路由规则数组 – 路径和组件名对应关系 5…...
全国自学考试03708《中国近现代史纲要》重点复习精要
1. 西方列强的殖民扩张和鸦片战争的影响。(两面性) :反面—破坏了了中国的小农经济,是中国由封建社会转变为两半社会。 --一系列不公平条约,破坏了中国主权领土完整。 --压迫中国人民,给中国人民带来了巨大…...
数据库面试题——锁
了解数据库的锁吗? 锁是数据库系统区别于文件系统的一个关键特性,锁机制用于管理对共享资源的并发访问。 InnoDB下两种标准行级锁: 共享锁(S Lock),允许事务读一行数据。 排他锁(X Lock&…...
Python笔记 -- 文件和异常
文章目录1、文件1.1、with关键字1.2、逐行读取1.3、写入模式1.4、多行写入2、异常2.1、try-except-else2.2、pass1、文件 1.1、with关键字 with关键字用于自动管理资源 使用with可以让python在合适的时候释放资源 python会将文本解读为字符串 # -*- encoding:utf-8 -*- # 如…...
蓝桥杯刷题冲刺 | 倒计时24天
作者:指针不指南吗 专栏:蓝桥杯倒计时冲刺 🐾马上就要蓝桥杯了,最后的这几天尤为重要,不可懈怠哦🐾 文章目录1.修剪灌木2.统计子矩阵1.修剪灌木 题目 链接: 修剪灌木 - 蓝桥云课 (lanqiao.cn) 找…...
真正理解微软Windows程序运行机制——什么是消息
我是荔园微风,作为一名在IT界整整25年的老兵,今天说说Windows程序的运行机制。经常被问到MFC到底是一个什么技术,为了解释这个我之前还写过帖子,但是很多人还是不理解。其实这没什么,我在学生时代也被这个问题困绕过。…...
HTTP 缓存的工作原理
缓存是解决http1.1当中的性能问题主要手段。缓存可能存在于客户端浏览器上,也可以存在服务器上面,当使用过期缓存可能给用户展示的是错误的信息而导致一些bug。 HTTP 缓存:为当前请求复用前请求的响应 • 目标:减少时延࿱…...
RK3568在Android上进行驱动模块开发(源码外)
文章目录 前言一、ARCH架构二、编译器三、建立自己的Makefile文件总结前言 本文记录在驱动开发时,由于编译内核时间较长,经常会选择单独编译一个模块,这里主要讲解,makefile文件如何编写(主要是编译器和架构) 提示:以下是本篇文章正文内容,下面案例可供参考 一、ARCH…...
操作技巧 | 在Revit中借用CAD填充图案的方法
在建模过程中,有时需要达到多种填充效果,而CAD中大量的二维填充图案,便是最直接的资源之一。 使用 填充图案之前 使用 填充图案之后 其中要用到主要命令便是对表面填充图案的添加与编辑 简单效果 如下 模型填充与绘图填充 区别 模型填…...
Java的二叉树、红黑树、B+树
数组和链表是常用的数据结构,数组虽然查找快(有序数组可以通过二分法查找),但是插入和删除是比较慢的;而链表,插入和删除很快(只需要改变一些引用值),但是查找就很慢&…...
昨天某读者拿到华为OD岗位offer,今天来分享一下经验,包含华为OD机试
来自读者投稿,已经拿到华为 OD 开发岗位 offer,询问了一些问题,下面是他的一些经验。 文章目录华为 OD 投递简历华为 OD 机试分数OD 机试通过之后,收到综合测评OD 技术面(时长 1 小时左右)主管/HR 面试&…...
树的遍历方式(前中后,层序遍历,递归,迭代,Morris遍历)-----直接查询代码
目录 一.前序遍历 1.递归 2.栈迭代 3.Morris遍历 二.中序遍历 1.递归 2.栈迭代 3.Morris遍历 三.后序遍历 1.递归 2.栈迭代 3.Morris遍历 四.前中后序的统一迭代法 1.前序遍历 2.中序遍历 3.后序遍历 五.层序遍历 1.队列迭代 2.之字形层序遍历 3.锯齿形层序…...
Docker Registry部署镜像私有仓库及鉴权认证
文章目录一、Docker Registry是什么?二、Docker Registry部署私有仓库2.1、Docker Registry安装2.2、Docker Registry配置2.3、启动Docker Registry2.4、Docker客户端配置2.5、向Docker Registry上传和下载镜像三、Docker Registry鉴权和认证3.1、基本认证3.2、Bear…...
stm32外设-中断详解
0. 写在最前 本栏目笔记都是基于stm32F10x 1. 中断是啥? 什么是中断:CPU在处理某一事件A时,发生的另外某一事件B请求CPU去处理(产生了中断),随后CPU暂时中断当前正在执行的任务,去对事件B进行处…...
第十四届蓝桥杯三月真题刷题训练——第 13 天
目录 第 1 题:特殊日期 问题描述 答案提交 运行限制 代码: 思路: 第 2 题:重合次数 问题描述 答案提交 运行限制 代码: 第 3 题:左移右移 问题描述 输入格式 输出格式 样例输入 样例输出…...
webgl_gpgpu_birds 样例分析
webgl_gpgpu_birds 是一个 three.js 的官方样例,这个例子模拟了鸟群的运动,是一个群组动画,并且动画的帧率也很高;鸟群的运动很自然,非常值得研究。类似的群组动画还有鱼群,boid是‘类鸟群’的英文 大概两…...
以业务行为驱动的反入侵安全能力建设
0x0 背景 最近听到一些甲方安全领域的专家分享了部分安全建设的经验,对安全运营下的反入侵技术能力建设有了些新的看法,依靠单个/多个异构的安全产品的关联能力形成的安全中台并不能在实际的攻防对抗当中占据主动地位,且很容易达到一个天花板…...
Unity3d C#使用DOTween插件的Sequence实现系列动画OnComplete无效和颜色设置无效的问题记录
前言 最近在弄一个文字动画效果的动画,使用了DOTween插件的Sequence来实现,主要就是对一个Text进行的文字打字、缩放和颜色设置等动画,功能是先对Text实现打字的动画,打字完成后,延时几秒对文字进行缩小、颜色变淡&am…...
【蓝桥杯-筑基篇】排序算法
🍓系列专栏:蓝桥杯 🍉个人主页:个人主页 目录 前言: 一、冒泡排序 二、选择排序 三、插入排序 四、图书推荐 前言: 算法工具推荐: 还在为数据结构发愁吗?这款可视化工具,帮助你更好的了解…...
MAA明日方舟自动化助手:5分钟快速上手完整指南
MAA明日方舟自动化助手:5分钟快速上手完整指南 【免费下载链接】MaaAssistantArknights 一款明日方舟游戏小助手 项目地址: https://gitcode.com/GitHub_Trending/ma/MaaAssistantArknights 还在为《明日方舟》重复刷图、基建管理而烦恼吗?MAA助手…...
不止于集成:在RuoYi-Camunda流程设计器中实现自定义属性面板与FEEL表达式校验
深度定制RuoYi-Camunda流程设计器:从属性面板扩展到FEEL表达式校验实战 当标准BPMN设计器无法满足复杂业务需求时,定制化开发成为必经之路。某跨国零售企业的审批系统曾因无法在流程节点上定义"区域经理审批阈值"字段,导致每次业务…...
英飞凌Aurix2G TC3XX 中断路由与DMA联动实战解析
1. 中断与DMA联动的核心价值 第一次接触英飞凌Aurix2G TC3XX的中断路由功能时,我像发现新大陆一样兴奋。传统嵌入式开发中,ADC采样完成→CPU读取数据→存入内存的流程就像用勺子一勺一勺地运水,而中断触发DMA的机制则像接上了自来水管——数据…...
Proteus8.9 安装避坑指南:从下载到稳定运行的完整流程
1. 为什么选择Proteus8.9? Proteus作为电子设计自动化(EDA)领域的经典工具,在单片机仿真和电路设计方面一直备受工程师和学生青睐。8.9版本之所以成为众多用户的首选,主要在于它对新型单片机的支持更加完善。比如STC15…...
Spark--一文了解SparkSql的Join策略
文章目录前言一、join 基本要素二、join 实现三、五种join 策略3.1 2 种数据分发模式(数据怎么到同一个节点)3.1.1 Broadcast Join(广播 Join,也叫 Map Join)3.1.2 Shuffle Join(重分区 Join,也…...
10个Twisted Web模块实战技巧:构建高性能HTTP服务器和客户端的终极指南
10个Twisted Web模块实战技巧:构建高性能HTTP服务器和客户端的终极指南 【免费下载链接】twisted Event-driven networking engine written in Python. 项目地址: https://gitcode.com/gh_mirrors/tw/twisted Twisted Web是基于Python的事件驱动网络引擎&…...
毕业季、返修季、投稿季:SCI论文润色,到底能不能提高接收率?
“SCI论文如果先润色,再投稿,是不是更容易被接收?”这个问题,真的每年到了这个时间点都会高频出现。尤其是3月底到4月初,很多同学刚从基金申请、毕业论文、返修修改的高压节奏里缓过来,马上又进入下一轮“赶…...
避开C盘爆满!保姆级教程:在D盘安装Unity 2023.2f1c1和VS2022社区版
避开C盘爆满!保姆级教程:在D盘安装Unity 2023.2f1c1和VS2022社区版 对于刚接触游戏开发的新手来说,安装Unity和Visual Studio往往是遇到的第一个"拦路虎"。更让人头疼的是,这两个"重量级"开发工具默认都会占…...
TWS耳机充电仓硬件设计全解析:从Type-C接口到NTC保护的7大核心模块
TWS耳机充电仓硬件设计全解析:从Type-C接口到NTC保护的7大核心模块 当你在咖啡馆掏出AirPods时,可能不会想到那个小巧的充电仓里藏着多少精密电路。作为硬件工程师,我们眼中的充电仓不是简单的塑料盒子,而是一个由七大核心模块组成…...
革新性英雄联盟效率工具:League-Toolkit为玩家打造智能游戏体验
革新性英雄联盟效率工具:League-Toolkit为玩家打造智能游戏体验 【免费下载链接】League-Toolkit 兴趣使然的、简单易用的英雄联盟工具集。支持战绩查询、自动秒选等功能。基于 LCU API。 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 在快节…...
