java-LinkedList源码详解

前言:

LinkedList 是 Java 中另一个常用的集合类,它基于双向链表实现,支持高效的插入和删除操作,但随机访问性能较差

类定义和成员变量:

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

    transient int size = 0; // 链表中的元素数量

    // 链表的头节点
    transient Node<E> first;

    // 链表的尾节点
    transient Node<E> last;

    // 内部静态类,表示链表的节点
    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;
        }
    }
}
  • size:链表中元素的数量。
  • first:链表的头节点。
  • last:链表的尾节点。
  • Node:内部静态类,表示链表的节点,包含数据 (item)、前驱节点 (prev) 和后继节点 (next)。

构造方法:

LinkedList 提供了两个构造方法:

java 复制代码
// 构造一个空链表
public LinkedList() {
}

// 构造一个包含指定集合元素的链表
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}
  • 无参构造方法:创建一个空链表。
  • 使用集合构造方法:创建一个包含指定集合元素的链表。

核心方法:

添加元素

java 复制代码
// 在链表末尾添加元素
public boolean add(E e) {
    linkLast(e);
    return true;
}

// 在链表末尾链接一个新节点
void linkLast(E e) {
    final Node<E> l = last; // 获取当前尾节点
    final Node<E> newNode = new Node<>(l, e, null); // 创建新节点
    last = newNode; // 更新尾节点为新节点
    if (l == null) // 如果链表为空
        first = newNode; // 新节点也是头节点
    else
        l.next = newNode; // 否则,将新节点链接到原尾节点的后面
    size++; // 链表大小增加
    modCount++; // 修改次数增加
}
  • add(E e):在链表末尾添加元素。
  • linkLast(E e):在链表末尾链接一个新节点。

获取元素

java 复制代码
// 获取指定索引位置的元素
public E get(int index) {
    checkElementIndex(index); // 检查索引是否越界
    return node(index).item;  // 返回节点的数据
}

// 检查索引是否越界
private void checkElementIndex(int index) {
    if (index < 0 || index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

// 返回指定索引位置的节点
Node<E> node(int index) {
    if (index < (size >> 1)) { // 如果索引在前半部分
        Node<E> x = first;
        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;
    }
}
  • get(int index):获取指定索引位置的元素。
  • node(int index):根据索引返回节点,优化了查找性能(从头部或尾部开始遍历)。

删除元素

java 复制代码
// 删除指定索引位置的元素
public E remove(int index) {
    checkElementIndex(index); // 检查索引是否越界
    return unlink(node(index)); // 删除节点并返回其数据
}

// 删除指定节点
E unlink(Node<E> x) {
    final E element = x.item; // 获取节点的数据
    final Node<E> next = x.next; // 获取后继节点
    final Node<E> prev = x.prev; // 获取前驱节点

    if (prev == null) { // 如果删除的是头节点
        first = next;
    } else {
        prev.next = next; // 将前驱节点的 next 指向后继节点
        x.prev = null;    // 清除当前节点的 prev 引用
    }

    if (next == null) { // 如果删除的是尾节点
        last = prev;
    } else {
        next.prev = prev; // 将后继节点的 prev 指向前驱节点
        x.next = null;    // 清除当前节点的 next 引用
    }

    x.item = null; // 清除当前节点的数据引用,帮助 GC
    size--;        // 链表大小减少
    modCount++;    // 修改次数增加
    return element;
}
  • remove(int index):删除指定索引位置的元素。
  • unlink(Node x):删除指定节点,并调整链表结构。

修改元素

java 复制代码
// 修改指定索引位置的元素
public E set(int index, E element) {
   checkElementIndex(index); // 检查索引是否越界
   Node<E> x = node(index);  // 获取节点
   E oldVal = x.item;        // 获取旧值
   x.item = element;         // 设置新值
   return oldVal;            // 返回旧值
}
  • set(int index, E element):修改指定索引位置的元素。

其他方法

队列操作

LinkedList 实现了 Deque 接口,支持队列和双端队列操作:

java 复制代码
// 在链表头部添加元素
public void addFirst(E e) {
    linkFirst(e);
}

// 在链表头部链接一个新节点
private void linkFirst(E e) {
    final Node<E> f = first; // 获取当前头节点
    final Node<E> newNode = new Node<>(null, e, f); // 创建新节点
    first = newNode; // 更新头节点为新节点
    if (f == null) // 如果链表为空
        last = newNode; // 新节点也是尾节点
    else
        f.prev = newNode; // 否则,将新节点链接到原头节点的前面
    size++; // 链表大小增加
    modCount++; // 修改次数增加
}

// 在链表尾部添加元素
public void addLast(E e) {
    linkLast(e);
}

// 获取链表头部元素
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;
}

工具方法

java 复制代码
// 返回链表大小
public int size() {
    return size;
}

// 判断链表是否为空
public boolean isEmpty() {
    return size == 0;
}

// 清空链表
public void clear() {
    for (Node<E> x = first; x != null; ) {
        Node<E> next = x.next;
        x.item = null;
        x.next = null;
        x.prev = null;
        x = next;
    }
    first = last = null;
    size = 0;
    modCount++;
}

序列化

LinkedList 实现了 Serializable 接口,支持序列化。由于 first 和 last 是 transient 的,LinkedList 自定义了序列化和反序列化的逻辑:

java 复制代码
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException {
    s.defaultWriteObject();

    s.writeInt(size); // 写入链表大小

    // 遍历链表,写入每个元素
    for (Node<E> x = first; x != null; x = x.next)
        s.writeObject(x.item);
}

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    s.defaultReadObject();

    int size = s.readInt(); // 读取链表大小

    // 遍历链表,读取每个元素并添加到链表中
    for (int i = 0; i < size; i++)
        linkLast((E) s.readObject());
}

总结

LinkedList 是一个基于双向链表实现的列表,支持高效的插入和删除操作,但随机访问性能较差。它的核心操作(如添加、删除、获取、修改)都是通过操作链表节点来实现的。

相关推荐
Little_Yuu22 分钟前
抽奖系统(基于Tkinter)
开发语言·python
深度物联网1 小时前
Spring Boot多模块划分设计
java·spring boot·后端
一 乐1 小时前
宿舍报修|宿舍报修小程序|基于Java微信小程序的宿舍报修系统的设计与实现(源码+数据库+文档)
java·数据库·微信小程序·小程序·论文·毕设·宿舍报修小程序
sword devil9001 小时前
基于python生成taskc语言文件--时间片轮询
开发语言·python
dudly1 小时前
用Python打造自己的专属命令行工具
开发语言·python·batch命令
AI+程序员在路上2 小时前
Qt6.8中进行PDF文件读取和编辑
开发语言·qt·pdf
球求了2 小时前
Linux 入门:操作系统&&进程详解
linux·运维·服务器·开发语言·学习
像风一样自由20202 小时前
PyQt5 到 PySide6 技术栈转换详解
开发语言·python·qt
Jack电子实验室2 小时前
基于MATLAB的图像色彩识别项目,彩色图像矩阵识别
开发语言·matlab·矩阵
Enti7c3 小时前
JavaScript 实现输入框的撤销功能
开发语言·javascript·ecmascript