LinkedList 源码分析

[LinkedList 简介](#LinkedList 简介)

LinkedList 是一个基于双向链表实现的集合类,经常被拿来和 ArrayList 做比较。关于 LinkedListArrayList的详细对比,我们 Java 集合常见面试题总结(上)有详细介绍到。

双向链表

不过,我们在项目中一般是不会使用到 LinkedList 的,需要用到 LinkedList 的场景几乎都可以使用 ArrayList 来代替,并且,性能通常会更好!就连 LinkedList 的作者约书亚 · 布洛克(Josh Bloch)自己都说从来不会使用 LinkedList

另外,不要下意识地认为 LinkedList 作为链表就最适合元素增删的场景。我在上面也说了,LinkedList 仅仅在头尾插入或者删除元素的时候时间复杂度近似 O(1),其他情况增删元素的平均时间复杂度都是 O(n) 。

[LinkedList 插入和删除元素的时间复杂度?](#LinkedList 插入和删除元素的时间复杂度?)

  • 头部插入/删除:只需要修改头结点的指针即可完成插入/删除操作,因此时间复杂度为 O(1)。
  • 尾部插入/删除:只需要修改尾结点的指针即可完成插入/删除操作,因此时间复杂度为 O(1)。
  • 指定位置插入/删除:需要先移动到指定位置,再修改指定节点的指针完成插入/删除,不过由于有头尾指针,可以从较近的指针出发,因此需要遍历平均 n/4 个元素,时间复杂度为 O(n)。

[LinkedList 为什么不能实现 RandomAccess 接口?](#LinkedList 为什么不能实现 RandomAccess 接口?)

RandomAccess 是一个标记接口,用来表明实现该接口的类支持随机访问(即可以通过索引快速访问元素)。由于 LinkedList 底层数据结构是链表,内存地址不连续,只能通过指针来定位,不支持随机快速访问,所以不能实现 RandomAccess 接口。

[LinkedList 源码分析](#LinkedList 源码分析)

这里以 JDK1.8 为例,分析一下 LinkedList 的底层核心源码。

LinkedList 的类定义如下:

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

LinkedList 继承了 AbstractSequentialList ,而 AbstractSequentialList 又继承于 AbstractList

阅读过 ArrayList 的源码我们就知道,ArrayList 同样继承了 AbstractList , 所以 LinkedList 会有大部分方法和 ArrayList 相似。

LinkedList 实现了以下接口:

  • List : 表明它是一个列表,支持添加、删除、查找等操作,并且可以通过下标进行访问。
  • Deque :继承自 Queue 接口,具有双端队列的特性,支持从两端插入和删除元素,方便实现栈和队列等数据结构。需要注意,Deque 的发音为 "deck" dɛk,这个大部分人都会读错。
  • Cloneable :表明它具有拷贝能力,可以进行深拷贝或浅拷贝操作。
  • Serializable : 表明它可以进行序列化操作,也就是可以将对象转换为字节流进行持久化存储或网络传输,非常方便。

LinkedList 类图

LinkedList 中的元素是通过 Node 定义的:

复制代码
private static class Node<E> {
    E item;// 节点值
    Node<E> next; // 指向的下一个节点(后继节点)
    Node<E> prev; // 指向的前一个节点(前驱结点)

    // 初始化参数顺序分别是:前驱结点、本身节点值、后继节点
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

初始化

LinkedList 中有一个无参构造函数和一个有参构造函数。

复制代码
// 创建一个空的链表对象
public LinkedList() {
}

// 接收一个集合类型作为参数,会创建一个与传入集合相同元素的链表对象
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

插入元素

LinkedList 除了实现了 List 接口相关方法,还实现了 Deque 接口的很多方法,所以我们有很多种方式插入元素。

我们这里以 List 接口中相关的插入方法为例进行源码讲解,对应的是add() 方法。

add() 方法有两个版本:

  • add(E e):用于在 LinkedList 的尾部插入元素,即将新元素作为链表的最后一个元素,时间复杂度为 O(1)。
  • add(int index, E element):用于在指定位置插入元素。这种插入方式需要先移动到指定位置,再修改指定节点的指针完成插入/删除,因此需要移动平均 n/2 个元素,时间复杂度为 O(n)。

获取元素

LinkedList获取元素相关的方法一共有 3 个:

  1. getFirst():获取链表的第一个元素。
  2. getLast():获取链表的最后一个元素。
  3. get(int index):获取链表指定位置的元素。

删除元素

LinkedList删除元素相关的方法一共有 5 个:

  1. removeFirst():删除并返回链表的第一个元素。
  2. removeLast():删除并返回链表的最后一个元素。
  3. remove(E e):删除链表中首次出现的指定元素,如果不存在该元素则返回 false。
  4. remove(int index):删除指定索引处的元素,并返回该元素的值。
  5. void clear():移除此链表中的所有元素。

遍历链表

推荐使用for-each 循环来遍历 LinkedList 中的元素, for-each 循环最终会转换成迭代器形式。

复制代码
LinkedList<String> list = new LinkedList<>();
list.add("apple");
list.add("banana");
list.add("pear");

for (String fruit : list) {
    System.out.println(fruit);
}

LinkedList 的遍历的核心就是它的迭代器的实现。

相关推荐
小欣加油11 小时前
leetcode2161 根据给定数字划分数组
数据结构·c++·算法·leetcode·职场和发展
程序员晨曦11 小时前
Java 并发修仙传:ThreadLocal 从“闭关修炼”到“走火入魔”的救赎之路
java·开发语言
AIGS00111 小时前
探索向量空间JBoltAI:工业企业数智化升级的基础设施
java·人工智能·人工智能ai大模型应用
zhangjw3411 小时前
第18篇:Java网络编程零基础详解,IP、端口、TCP、UDP、Socket通信、实战文件传输
java·网络·tcp/ip
中小企业实战军师刘孙亮11 小时前
快消纺织五金怎么融合?三大业态协同发展战略思路-佛山鼎策创局破局增长咨询
学习·面试·创业创新·制造·学习方法
我命由我1234511 小时前
Java 开发 - Jar 包与 War 包
java·开发语言·java-ee·intellij-idea·jar·idea·intellij idea
Upsy-Daisy11 小时前
Hermes Agent 学习笔记 04:工具调用系统,让 Agent 从“会说”变成“会做”
java·笔记·学习
Volunteer Technology11 小时前
SpringSecurity请求流转的本质
java·spring
心之伊始11 小时前
Spring AI MCP Client 实战:让 Java 后端通过 stdio 调用本地工具服务
java·spring boot·agent·spring ai·mcp
plainGeekDev12 小时前
文件读写(Java IO)→ Kotlin 扩展函数
android·java·kotlin