LinkedList底层结构和源码分析(JDK1.8)
参考视频:韩顺平Java集合
特点
- LinkedList 底层实现了
双向链表和双端队列的特点。 - 可以添加任意元素(元素可以重复),包括 null。
- 线程不安全,没有实现同步。
LinkedList 底层结构

- LinkedList 底层维护了一个双向链表
- LinkedList 中维护了两个属性
first和last分别指向首节点和尾节点。 - 每个节点(Node 对象)里面有维护了
prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表。 - 所以 LinkedList 的元素的添加和删除,不是通过数组完成的,相对来说效率较高。
模拟一个简单的双向链表
- 定义一个Node类,表示一个双向链表的节点:
public class Node { public Object item; public Node prev;//指向后一个节点 public Node next;//指向后一个节点 public Node(Object name){ this.item = name; } @Override public String toString() { return "Node name="+item; } } - 创建几个节点,并为其建立链表链接:
//模拟一个简单的双向链表 @Test public void test1(){ LinkedList list = new LinkedList(); Node node1 = new Node("Node1"); Node node2 = new Node("Node2"); Node node3 = new Node("Node3"); //链接三个节点,形成双向链表 //1->2->3 node1.next = node2; node2.next = node3; //3->2->1 node3.prev = node2; node2.prev = node1; //头节点 Node first = node1; //尾节点 Node last = node3; } - 简单的应用:从头到尾遍历、从尾到头遍历:
System.out.println("=======从头到尾进行遍历======"); //演示:从头到尾进行遍历 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.prev; } - 简单的应用:在 node1 和 node2 之间插入 node 1.5
Node node15 = new Node("node1.5"); node15.prev = node1; node15.next = node2; node1.next = node15; node2.prev = node15; - 遍历一下验证插入结果
- (但是注意,这里所展示的遍历的例子是依靠 first 和 last 指针的移动来实现的,所以如果我们要遍历一遍插入新元素后的链表,则需要将 first 和 last 进行一个重新指向。)
//头和尾要重新进行指向 first = node1; last = node3; System.out.println("=======从头到尾进行遍历======"); //演示:从头到尾进行遍历 while(true){ if(first == null)break; System.out.println(first); first = first.next;//头节点移动至下一个 }
LinkedList 增删改查——源码示例
- 源码:
- 构造器创建 linkedList 的细节和流程
- 第一次添加元素(节点 1)
- 第二次添加元素(节点 2)
- 删除第一个节点
- 删除下标为 2 的节点
@Test public void test2(){ LinkedList list = new LinkedList(); list.add(1); list.add(2);System.out.println("linkedList = "+list); }
在 JDK 1.8 中,Node 的类是这样设计的:

与前面我们的简单实现相比,这里的 Node 中存放数据的 item 是使用泛型来约束的,这样在使用时可以定义其存储数据的类型。更加严谨一些。
构造器创建 LinkedList
- 调用空参构造器创建了一个 size=0 的 linkedList。
- 此时头和尾都为空,
modCount为修改次数

第一次添加元素(节点 1)
- 进入
add()方法,将所添加的数据传递给linkedLast()中

- 注意此时链表为空,所以头节点和尾节点都为 null

LinkedLast()作用是将所添加的元素插在最后:

- var2=链表当前最后的元素。(因为此时链表为空,所以 var2=null)
- 新建一个 Node,将其中的 item 值设置为 var1,并设置前节点(var2)和后节点(null)。这就是 var3。(此时 var2【null】➡var3【item=var1】➡null)
- 让当前的尾节点指向 var3。(此时last➡var3)
- 判断前一个节点( var2) 是否为空?
- 如果 var2=null,也就代表原本是一个空链表,那么我们就需要将头结点指向新添加的节点 var3。(此时first➡var3⬅️last)
- 如果 var2 不为空,就将 var2 的下一个节点指向 var3。
- 链表长度+1 (此时size=1)
- 修改次数+1 (此时monCount=1)
- 至此,添加操作完成,回到添加的主逻辑:返回 true,添加完成。

- 可以看到数据已经放在了链表中,并且头节点和尾节点都指向该节点 (@861)。

第二次添加元素(节点 2)
- 进入
add()方法,将所添加的数据传递给linkedLast()中:

LinkedLast()作用是将所添加的元素插在最后:

- var2=链表当前最后的元素。(此时 var2=节点 1)
- 新建一个 Node,将其中的 item 值设置为 var1,并设置前节点(var2)和后节点(null)。这就是 var3。(此时 var2【节点 1】➡var3【item=var1】➡null)
- 让当前的尾节点指向 var3。(此时last➡var3)
- 判断前一个节点( var2) 是否为空?
- 如果 var2=null,也就代表原本是一个空链表,那么我们就需要将头结点指向新添加的节点 var3。
- 如果 var2 不为空,就将 var2 的下一个节点指向 var3。(此时 var2【节点 1】➡var3)
- 链表长度+1 (此时size=2)
- 修改次数+1 (此时monCount=2)
- 至此,添加操作完成,回到添加的主逻辑:返回 true,添加完成。

- 可以看到数据已经放在了链表中,并且头节点指向节点 1(@861),尾节点指向节点 2 (@867)。

删除第一个节点(无参,默认删除第一个)
- 进入
remove()方法中,可以发现其中调用了removeFirst()方法:

removeFirst()的作用就是删除链表的第一个元素。- var1=当前链表的头结点==(节点 1)==
- 判断头结点是否为空,也就是判断链表是否为空
- 为空则抛出异常;不为空则调用
unlinkFirst()方法,将头节点传入:

- unlinkFirst() 的作用是删除链表的第一个节点:
- var2=头节点中的数据 (本例中是 “1”)
- var3=头节点的下一个节点 (本例中是节点 2)
- 将头节点的数据清空
- 将头节点的 next 置为 null;
- 将头节点设置为下一个节点(var3)
- 判断下一个节点是否为空(也就是判断该链表是否只有 var1 这一个节点)
- 为空,则代表尾指针 last 也在 var1 这个要删除的节点上,所以需要将 last 也置为 null;
- 不为空,则表示后续还有节点,把后节点的 prev 设置为 null。(本例)
- 链表的长度-1 (size=9-1=8)
- 修改次数+1 (modCount=9+1=10)

- 可以看到第一个节点已被删除。

删除索引为 2 的节点
此时链表中的元素有【2,3,4,5,6,7,8,9】,我们要删除的是 “4”。
- 进入
remove()方法,先进入checkElementIndex()方法检查一下索引是否合法

checkElementIndex():检查索引是否合法 (目前链表 size=8,索引2 合法)


- 回到 remove() 中,调用 unlink(node) 方法,其作用是删除一个节点 node。

- 但由于我们传入的是索引,而不是节点本身,所以需要调用
node (index)方法来查找并返回节点。 - 观察 node 方法:它的查找方式是检查要查找的节点索引在前半部分还是后半部分。(本例中我们查找索引 2,在前半部分,所以从头开始查找)
- 如果是前半部分则从头开始查找;
- 如果是后半部分,则从尾开始查找。
- var2 是遍历用的节点
- var3 遍历用的索引

- 最后将找到的节点返回给
unlink(node)方法中。

- 找到的节点作为参数 var1 传入该方法。
- var2=该节点的数据 (本例中是 “4”)
- var3=该节点的后节点
- var4=该节点的前节点
- 判断前节点(var4)是否为 null
- 如果 var4 为 null,则表示该节点为头节点,需要将 first 指针指向其下一个节点(var3);
- 如果不为 null,说明其前面还有节点,就要将前节点(var4)的 next 指向其后节点(var3),并且将该节点的 prev 解除。(本例)
- 判断后节点(var3)是否为 null
- 如果 var3 为 null,则表示该节点为尾节点,需要将 last 指针指向其上一个节点(var3);
- 如果不为 null,说明其后面还有节点,就要将后节点(var3)的 prev 指向其前节点(var4),并且将该节点的 next 解除。(本例)
- 这样一来就将要删除的元素 var1 彻底与链表解除了关系,成为了一个孤立的节点。最后要做的就是将其中的数据(item)也进行清空
- 链表长度-1 (size=8-1=7)
- 修改次数+1 (modCount=10+1=11)

- 至此,删除下标为 2 的节点完成。
改:set (int, Object) 和查:get(i) 的源码都比较简单,可以自己分析一下。
LinkedList 遍历
由于 LinkedList 实现了 List 接口,而 List 接口可以使用三种方式遍历:迭代器、增强 for 循环、普通 for 循环。所以 LinkedList 也是支持的。
//增强for遍历
System.out.println("======增强遍历");
for (Object o :list) { System.out.println(o);
} //迭代器遍历
System.out.println("======迭代器遍历");
Iterator iterator = list.iterator();
while(iterator.hasNext()){ Object next = iterator.next(); System.out.println(next);
} //普通for遍历
System.out.println("======普通for遍历");
for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i));
}
相关文章:
LinkedList底层结构和源码分析(JDK1.8)
参考视频:韩顺平Java集合 特点 LinkedList 底层实现了 双向链表 和 双端队列 的特点。可以添加任意元素(元素可以重复),包括 null。线程不安全,没有实现同步。 LinkedList 底层结构 LinkedList 底层维护了一个双向链…...
数字内容体验的技术支柱是什么?
数据分析引擎构建基础 数字内容体验的技术底座始于对海量用户行为数据的深度解析。作为技术体系的根基,数据分析引擎通过实时采集、清洗与结构化处理,将分散的点击轨迹、停留时长及交互偏好转化为可操作的洞察。其核心能力体现在三方面:一是…...
C# 使用Markdown2Pdf把md文件转换为pdf文件
NuGet安装Markdown2Pdf库,可以把格式简单markdown文件转换为pdf。但该库用了Puppeteer Sharp,因此会在运行过程中提示指定Chrome浏览器路径或自动下载Chrome浏览器。 代码如下: using Markdown2Pdf;var converter new Markdown2PdfConverte…...
专家系统如何运用谓词逻辑进行更复杂的推理
前文,我们讲解了命题逻辑和谓词逻辑的基本概念、推理规则、应用以及一些简单的示例。具体内容可以先看我的文章:人工智能的数学基础之命题逻辑与谓词逻辑(含示例)-CSDN博客 那么形如专家系统这类复杂系统,是如何通过谓…...
html css网页制作成品——糖果屋网页设计(4页)附源码
目录 一、👨🎓网站题目 二、✍️网站描述 三、📚网站介绍 四、🌐网站效果 五、🪓 代码实现 🧱HTML 六、🥇 如何让学习不再盲目 七、🎁更多干货 一、👨…...
Ubuntu上部署Flask+MySQL项目
一、服务器安装python环境 1、安装gcc(Ubuntu默认已安装) 2、安装python源码 wget https://www.python.org/ftp/python/3.13.2/Python-3.13.2.tar.xz 3、安装Python依赖库 4、配置python豆瓣源 二、服务器安装虚拟环境 1、安装virtualenv pip3.10 ins…...
落雪音乐Pro 8.8.6 | 内置8条音源,无需手动导入,纯净无广告
洛雪音乐Pro版内置多组稳定音源接口,省去手动导入的繁琐操作,安装即可畅听海量音乐。延续原版无广告的纯净体验,支持歌单推荐与音源切换,满足个性化听歌需求。此版本仅支持在线播放,无法下载音乐,且与原版不…...
什么是全栈?
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点下班 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 📃文章前言 🔷文章均为学习工…...
一些docker命令
一、基础命令 查看 Docker 版本 docker --version 或 docker version:显示 Docker 客户端和服务器的版本信息。 查看 Docker 系统信息 docker info:显示 Docker 系统的详细信息,包括镜像、容器数量、存储驱动类型等。 Docker 服务管理 s…...
《DeepSeek 开源 DeepGEMM:开启AI计算新时代的密钥》:此文为AI自动生成
《DeepSeek 开源 DeepGEMM:开启AI计算新时代的密钥》:此文为AI自动生成 引言:AI 计算的新曙光 在当今科技飞速发展的时代,人工智能(AI)无疑是最为耀眼的领域之一。从语音助手到自动驾驶,从图像…...
OpenCV实现图像特征提取与匹配
一、特征检测与描述子提取 选择特征检测器 常用算法包括: ORB:一种高效的替代SIFT和SURF的算法,主要用于移动机器人和增强现实等领域。适合实时应用,结合FAST关键点与BRIEF描述子。SIFT(尺度不变特征变…...
将分支`XXX`合并到远程分支`master
将分支feat-task合并到远程分支master 首先,切换到本地的 master 分支 git checkout master确保你的本地 master 分支是最新的,拉取远程的更新 git pull origin master将 feat-task 分支的代码合并到 master 分支 git merge feat-task如果在合并过程…...
程序化广告行业(13/89):DSP的深入解析与运营要点
程序化广告行业(13/89):DSP的深入解析与运营要点 大家好!一直以来,我都对程序化广告行业保持着浓厚的学习兴趣,在探索的过程中积累了不少心得。今天就想把这些知识分享出来,和大家一起学习进步…...
XML文件格式的简介及如何用Python3处理XML格式对象
诸神缄默不语-个人技术博文与视频目录 文章目录 1. XML格式简介2. 格式化XML文件的工具3. Python处理XML:xml库1. xml.etree.\(c\)ElementTree2. xml.dom.minidom 4. 本文撰写过程中参考的其他网络资料 1. XML格式简介 可扩展标记语言 (Extensible Markup Language…...
通过qemu仿真树莓派系统调试IoT固件和程序
通过qemu仿真树莓派系统调试IoT固件和程序 本文将介绍如何使用 QEMU 模拟器在 x86 架构的主机上运行 Raspberry Pi OS(树莓派操作系统)。我们将从下载镜像、提取内核和设备树文件,到启动模拟环境,并进行一些常见的操作࿰…...
Oracle底层原理解析
Oracle 解析 1、union \ union all \ Intersect \ Minus内部处理机制(优化) 当查询语句中的where子句中使用到or时,可以用union all来代替。因为使用or查询语句的时候,引起全表扫描,并走索引查询 特别:当…...
深度解读DeepSeek部署使用安全(48页PPT)(文末有下载方式)
深度解读DeepSeek:部署、使用与安全 详细资料请看本解读文章的最后内容。 引言 DeepSeek作为一款先进的人工智能模型,其部署、使用与安全性是用户最为关注的三大核心问题。本文将从本地化部署、使用方法与技巧、以及安全性三个方面,对Deep…...
【前端三剑客】万字总结JavaScript
一、初识JavaScript 1.1 JavaScript 的作用 表单动态校验(密码强度检测) ( JS 产生最初的目的 )网页特效服务端开发(Node.js)桌面程序(Electron)App(Cordova)控制硬件-物联网(Ruff)游戏开发(cocos2d-js) 1.2 HTML/CSS/JS 的关系…...
【哈希表与字符串的算法之路:思路与实现】—— LeetCode
文章目录 两数之和面试题01.02.判定是否为字符重排存在重复元素存在重复元素||字母异位词分组最长公共前缀和最长回文子串二进制求和字符串相乘 两数之和 这题的思路很简单,在读完题目之后,便可以想到暴力枚举,直接遍历整个数组两遍即可&…...
基于Android的记事本APP设计与实现:从需求分析到功能实现(超级简单记事本,附源码+文档报告)
基于Android的记事本APP设计与实现:从需求分析到功能实现 (以前大学课堂作业,抄在这里当个回忆吧) 引言 随着社会的不断进步,信息化建设不断发展,电子文字输入在生活、学习、工作中占有越来越重要的作用…...
eNSP中路由器的CON/AUX接口、GE Combo接口、Mini USB接口、USB接口、WAN侧uplink接口、FE接口、GE接口介绍
路由器常见接口的详细介绍及其应用示例: 1. CON/AUX 接口 全称:Console/Auxiliary(控制台/辅助接口)作用: CON(Console):通过命令行界面(CLI)直接配置路由器…...
Hello Mr. My Yesterday日文歌词附假名注音,祭奠逝去的青春
hello mr. my yesterday Hundred Percent Free Hello Mr. my yesterday云っておくれよ “夢叶うその瞬間にまた逢える”と 前方の幾多前途多難の未知 後方の道後悔も知った 経験と価値 夢なかば 一本の道結果だが ひとつだけ知りたいよ 神様がいるのなら “幸せの定義っ…...
ubuntu ollama+dify实践
安装ollama 官网的指令太慢了,使用以下指令加速: export OLLAMA_MIRROR"https://ghproxy.cn/https://github.com/ollama/ollama/releases/latest/download" curl -fsSL https://ollama.com/install.sh | sed "s|https://ollama.com/dow…...
S7-1200 G2移植旧版本S7-1200程序的具体方法示例
S7-1200 G2移植旧版本S7-1200程序的具体方法示例 前期概要: S7-1200 G2必须基于TIA博途V20,之前的程序可通过移植的方式在新硬件上使用。 该移植工具可自动将TIA Portal 项目从 S7-1200 移植到更新的S7-1200 G2。 注意: 该插件支持在同一TIA Portal项目实例内将软件和/或硬…...
新办公室哪款空气净化器除甲醛效果好?高效除甲醛,提升效率
现代办公环境中,空气质量对员工的健康与工作效率产生着不可忽视的影响。尤其是新装修的办公室,往往因为空气中的甲醛浓度超标而导致一系列健康问题。因此,选择一款性能优越的除甲醛空气净化器就显得尤为重要。合适的空气净化器不仅可以有效过…...
塑造企业数字化形象:企业信息化UI界面设计的关键要素
引言 在数字化转型的大潮中,企业信息化系统的UI(用户界面)界面设计不仅是技术实现的最后一环,更是塑造企业数字化形象、提升用户体验、增强业务效率的重要手段。优秀的UI设计能够直观展现企业价值观,提升用户粘性&…...
大视频背景暗黑风格的wordpress企业主题免费下载
整体风格是黑色的,首页首屏大视频背景,动态效果非常好。向下滚动时,滚动的特效也不错。 原文 https://www.bixugao.com/wp/26.html...
CUDA编程之内存零拷贝技术
一、实现原理 零拷贝内存通过将主机锁页内存直接映射到设备地址空间,实现CPU与GPU共享内存,避免显式数据拷贝。锁页内存通过cudaHostAlloc或cudaHostRegister分配,确保物理地址固定且不被操作系统换页,从而支持DMAÿ…...
C语言基础知识04
指针 指针概念 指针保存地址,地址是字节的编号 指针类型和保存的地址类型要一直 使用时注意,把地址转换为&变量的格式来看 int a[3]; a转为&a[0] 指针的大小 64bit 固定8字节, 32bit 固定4字节 指针…...
在 Java 中,== 和 equals 的区别
1. 运算符 作用:比较两个对象的 内存地址(引用类型)或 值(基本数据类型)。 适用场景: 基本数据类型(int, char, boolean 等):直接比较值是否相等。 引用类型ÿ…...
