Java集合篇之深入解析LinkedList

news2025/1/17 3:56:41

写在开头

作为ArrayList的同门师兄弟,LinkedList的师门地位逊色不少,除了在做算法题的时候我们会用到它之外,在实际的开发工作中我们极少使用它,就连它的创造者都说:“I wrote it,and I never use it”,想想颇有点好笑,但这并不影响我们去学习它,个人认为它底层的链表逻辑对于我们代码思想的培养还是挺有帮助的。在这里插入图片描述

源码解析

看过build哥文章的同学应该都知道,俺喜欢通过源码去学习和分析对象或代码逻辑,因此,话不多说,直接上源码!

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
  //...
}

如上为JDK8中LinkedList的继承实现关系,通过这些关系我们可以大致分析出它所具备的特性:

  1. 实现List接口 表明它是一个列表,支持添加、删除、查找等操作,并且可以通过下标进行访问;
  2. Deque继承自 Queue 接口,具有双端队列的特性,支持从两端插入和删除元素,方便实现栈和队列等数据结构;
  3. Cloneable :表明它具有拷贝能力,可以进行深拷贝或浅拷贝操作;
  4. Serializable : 表明它可以进行序列化操作,也就是可以将对象转换为字节流进行持久化存储或网络传输,非常方便。

LinkedList提供了非常多的方法供我们使用,继续阅读源码可以看到

// 在链表尾部插入元素
public boolean add(E e) {
    linkLast(e);
    return true;
}

// 在链表指定位置插入元素
public void add(int index, E element) {
    // 下标越界检查
    checkPositionIndex(index);

    // 判断 index 是不是链表尾部位置
    if (index == size)
        // 如果是就直接调用 linkLast 方法将元素节点插入链表尾部即可
        linkLast(element);
    else
        // 如果不是则调用 linkBefore 方法将其插入指定元素之前
        linkBefore(element, node(index));
}

// 将元素节点插入到链表尾部
void linkLast(E e) {
    // 将最后一个元素赋值(引用传递)给节点 l
    final Node<E> l = last;
    // 创建节点,并指定节点前驱为链表尾节点 last,后继引用为空
    final Node<E> newNode = new Node<>(l, e, null);
    // 将 last 引用指向新节点
    last = newNode;
    // 判断尾节点是否为空
    // 如果 l 是null 意味着这是第一次添加元素
    if (l == null)
        // 如果是第一次添加,将first赋值为新节点,此时链表只有一个元素
        first = newNode;
    else
        // 如果不是第一次添加,将新节点赋值给l(添加前的最后一个元素)的next
        l.next = newNode;
    size++;
    modCount++;
}

// 在指定元素之前插入元素
void linkBefore(E e, Node<E> succ) {
    // assert succ != null;断言 succ不为 null
    // 定义一个节点元素保存 succ 的 prev 引用,也就是它的前一节点信息
    final Node<E> pred = succ.prev;
    // 初始化节点,并指明前驱和后继节点
    final Node<E> newNode = new Node<>(pred, e, succ);
    // 将 succ 节点前驱引用 prev 指向新节点
    succ.prev = newNode;
    // 判断尾节点是否为空,为空表示当前链表还没有节点
    if (pred == null)
        first = newNode;
    else
        // succ 节点前驱的后继引用指向新节点
        pred.next = newNode;
    size++;
    modCount++;
}
// 获取链表的第一个元素
public E getFirst() {
    final Node<E> f = first;
    if (f == null)
        throw new NoSuchElementException();
    return f.item;
}

// 获取链表的最后一个元素
public E getLast() {
    final Node<E> l = last;
    if (l == null)
        throw new NoSuchElementException();
    return l.item;
}

// 获取链表指定位置的元素
public E get(int index) {
  // 下标越界检查,如果越界就抛异常
  checkElementIndex(index);
  // 返回链表中对应下标的元素
  return node(index).item;
}

在这里插入图片描述
更多的API方法可以参考:LinkedList全量方法

使用LinkedList

在Java中我们写一个小测试代码来用一下LinkedList的增删改查

【代码示例1】

  // 创建LinkedList集合
  LinkedList link = new LinkedList();
  // 1、添加元素
  link.add("happy");
  link.add("new");
  link.offer("year"); // 向集合尾部追加元素
  link.push("javabuild"); // 向集合头部添加元素
  System.out.println(link); // 输出集合中的元素
  // 2、获取元素
  Object object = link.peek(); //获取集合第一个元素
  System.out.println(object); // 输出集合中的元素
  // 3、删除元素
  link.removeFirst(); // 删除集合第一个元素
  link.pollLast(); // 删除集合最后一个元素
  System.out.println(link);

输出:

[javabuild, happy, new, year]
javabuild
[happy, new]

对比ArrayList

  1. ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
  2. ArrayList 底层使用的是 Object 数组;LinkedList 底层使用的是双向链表数据结构;
  3. LinkedList 不支持高效的随机元素访问,而 ArrayList(实现了 RandomAccess 接口) 支持。
  4. ArrayList存在扩容问题,LinkedList不存在,直接放在集合尾部,修改指针即可;

提问:为什么LinkedList不支持高效的随机访问,或者说为什么不去实现RandomAccess 接口?

我们看过RandomAccess 接口的底层的同学知道,这个接口也是个标识性接口,只要实现了这个接口就意味着支持通过索引访问元素。由于 LinkedList 底层数据结构是链表,内存地址不连续,只能通过指针来定位,不支持随机快速访问,所以不能实现 RandomAccess 接口。
但是!
在LinkedList中依旧提供了get(int index):获取链表指定位置的元素。

// 获取链表指定位置的元素
public E get(int index) {
  // 下标越界检查,如果越界就抛异常
  checkElementIndex(index);
  // 返回链表中对应下标的元素
  return node(index).item;
}

源码中get方法实现通过位置获取元素的核心是node(index)方法,我们跟进去继续看一下!

// 返回指定下标的非空节点
Node<E> node(int index) {
    // 断言下标未越界
    // assert isElementIndex(index);
    // 如果index小于size的二分之一  从前开始查找(向后查找)  反之向前查找
    if (index < (size >> 1)) {
        Node<E> x = first;
        // 遍历,循环向后查找,直至 i == index
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

该方法中通过传入的index参数和size的1/2进行比较,小于则从链表头向后查找,否则从链表尾向前遍历查找,这与ArrayList中的get(index)方法还是有本质上的区别!

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

在这里插入图片描述

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

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

相关文章

MySQL之json数据操作

1 MySQL之JSON数据 总所周知&#xff0c;mysql5.7以上提供了一种新的字段格式json&#xff0c;大概是mysql想把非关系型和关系型数据库一口通吃&#xff0c;所以推出了这种非常好用的格式&#xff0c;这样&#xff0c;我们的很多基于mongoDB的业务都可以用mysql去实现了。当然…

Java 基于微信小程序的汽车4S店客户管理系统

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

C++类和对象-多态->案例1计算器类、案例2制作饮品、案例3电脑组装需求分析和电脑组装具体实现

#include<iostream> using namespace std; #include<string> //分别利用普通写法和多态技术实现计算器 //普通写法 class Calculator { public: int getResult(string oper) { if (oper "") { return m_Num1 m_Num2; …

引入成熟的Pytest自动化测试框架

虽然我们能使用脚本编写自动化测试框架&#xff0c;但没有必要重复找车轮子&#xff0c;引入成熟的自动化测试框架即可&#xff0c; Pytest是目前最成熟、功能最全面的Python测试框架之一&#xff0c;简单灵活、易于上手&#xff0c;可完全兼容其他测试框架如unitest&#xff…

普中51单片机学习(十)

动态数码管 数码管显示原理 动态显示的特点是将所有数码管的段选线并联在一起&#xff0c;由位选线控制是哪一位数码管有效。选亮数码管采用动态扫描显示。所谓动态扫描显示即轮流向各位数码管送出字形码和相应的位选&#xff0c;利用发光管的余辉和人眼视觉暂留作用&#xf…

SSTI模板注入漏洞(vulhub 复现)

首先了解模板引擎&#xff1a; 模板引擎&#xff08;这里特指用于Web开发的模板引擎&#xff09;是为了使用户界面与业务数据&#xff08;内容&#xff09;分离而产生的&#xff0c;它可以生成特定格式的文档&#xff0c;利用模板引擎来生成前端的html代码&#xff0c;模板引擎…

OpenAI:Sora视频生成模型技术报告(中文)

概述 视频生成模型作为世界模拟器 我们探索视频数据生成模型的大规模训练。具体来说&#xff0c;我们在可变持续时间、分辨率和宽高比的视频和图像上联合训练文本条件扩散模型。我们利用transformer架构&#xff0c;在视频和图像潜在代码的时空补丁上运行。我们最大的模型Sor…

HCIA-HarmonyOS设备开发认证V2.0-IOT硬件子系统-UART

目录 一、UART 概述二、UART 模块相关API三、UART 接口调用实例四、UART HDF驱动开发4.1、开发步骤(待续...) 坚持就有收获 一、UART 概述 UART 是通用异步收发传输器&#xff08;Universal Asynchronous Receiver/Transmitter&#xff09;的缩写&#xff0c;是通用串行数据总…

Chrome浏览器安装Axure-Chrome-Extension插件

Chrome浏览器打开Axure生成的HTML静态文件页面时&#xff0c;会显示如下图AXURE RP EXTENSION FOR CHROME&#xff0c;这是因为Chrome浏览器没有安装Axure插件Axure-Chrome-Extension导致的。 解决方法&#xff1a; 插件下载地址&#xff1a;https://download.csdn.net/downlo…

CleanMyMacX需要付费吗?多少钱?有哪些新功能

CleanMyMac X是一个付费应用程序**&#xff0c;需要许可证或订阅来解锁所有功能。不过&#xff0c;CleanMyMac X提供免费试用版供您访问其有限的功能。在试用模式下&#xff0c;用户可以使用部分功能进行体验&#xff0c;但这并非完全免费&#xff0c;因为某些功能会受到限制。…

Android下SF合成流程重学习之GPU合成

Android下SF合成流程重学习之GPU合成 引言 SurfaceFlinger中的图层选择GPU合成(CLIENT合成方式)时&#xff0c;会把待合成的图层Layers通过renderengine(SkiaGLRenderEngine)绘制到一块GraphicBuffer中&#xff0c;然后把这块GraphicBuffer图形缓存通过调用setClientTarget传递…

代码随想录算法训练营 DAY20 | 二叉树(7)

一、LeetCode 530 二叉搜索树的最小绝对值 题目链接&#xff1a;530.二叉搜索树的最小绝对值https://leetcode.cn/problems/minimum-absolute-difference-in-bst/ 思路一&#xff1a;利用搜索二叉树的中序遍历结果为有序数组的性质&#xff0c;将遍历结果保存到数组中&#xf…

分析:香港2亿港元诈骗案的风险特征及技术检测思路

目录 深度伪造带来的新挑战 如何有效检测深度伪造&#xff1f; 多管齐下的安全策略 据香港公共广播公司报道&#xff0c;一家跨国公司香港分行的一名员工在一次电话会议后被骗支付了2亿港元&#xff08;超过2500万美元&#xff09;的资金。 据介绍&#xff0c;这起骗局始于202…

【深度优先搜索】【树】【有向图】【推荐】685. 冗余连接 II

LeetCode685. 冗余连接 II 在本问题中&#xff0c;有根树指满足以下条件的 有向 图。该树只有一个根节点&#xff0c;所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点&#xff0c;而根节点没有父节点。 输入一个有向图&#xff0c;该图由…

Python学习(16)|列表_遍历_排序_max_min_sum

列表的遍历&#xff1a; a [10,20,30,40] for obj in a: #obj 是临时变量名称&#xff0c;随意起名print(obj) 执行结果&#xff1a; 复制列表所有的元素到新列表对象&#xff1a; list1 [30,40,50] list2 list1 #只是将list2也指向了列表对象。也就是说list…

基于Python3的数据结构与算法 - 01 复杂度和列表查找

一、时间复杂度 定义&#xff1a;用来评估算法运行效率的一个式子。 例如&#xff1a;此处的O(1) 详单与一个时间单位 接下来我们看下面两个式子&#xff1a; 如果按照上面的定义&#xff0c;那么打印三次相当O(3)&#xff0c;下面的循环相当于O(n21) 但是实际不是这样的 因…

YOLO v5项目实战 P5 解决运行detect文件时设置了--view-img但是显示不出来的问题

up主讲的实时显示目标检测后的图片的两种方法&#xff1a; &#xff08;1&#xff09;在下面的Terminal中输入下列命令&#xff1a; python detect.py --view-img &#xff08;2&#xff09;点击进入右上方的detect的Edit Configurations 然后在这个参数这里输入 --view img…

Pulsar-架构与设计

Pulsar架构与设计 一、背景和起源二、框架概述1.设计特点2.框架适用场景 三、架构图1.Broker2.持久化存储&#xff08;Persistent storage&#xff09;3.Pulsar元数据&#xff08;Metadata store&#xff09; 四、功能特性1.消息顺序性2.消息回溯3.消息去重4.消息重投递5.消息重…

一键安装ROS适用于Ubuntu22/20/18

一键安装ROS适用于Ubuntu22/20/18 1、简介 ROS&#xff08;Robot Operating System&#xff0c;机器人操作系统&#xff09;是一个用于机器人软件开发的框架。它提供了一套工具和库&#xff0c;用于机器人应用程序的开发、测试和部署。ROS是由美国斯坦福大学机器人实验室&…

银河麒麟操作系统自动同步时间更新

1、银河麒麟操作系统基于Centos8的&#xff0c;因centos8取消了ntp服务器&#xff0c;所以导致之前使用ntpdate命令无法同步时间 2、centos默认使用chrony模块来进行同步时间 3、修改chrony配置同步时间服务器 vim /etc/chrony.conf 4、目前使用的是阿里云的时间服务器&…