跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

Java LinkedList 底层原理与手动实现指南

本文探讨 Java 集合框架中的 LinkedList,分析其基于双向链表的底层结构及核心接口 List 与 Deque。通过对比 ArrayList 说明随机访问与增删操作的效率差异。同时提供手动实现单向链表的关键代码示例,涵盖节点定义、头尾插法、查找删除及遍历方法,帮助开发者深入理解链表机制与 Java 集合源码设计思路。

怪力乱神发布于 2026/3/29更新于 2026/7/2446 浏览
Java LinkedList 底层原理与手动实现指南

概述

链表是数据结构中线性表的常见实现方式,而 Java 中的 LinkedList 正是基于双向链表实现的集合类。在掌握顺序表(如 ArrayList)的基础上,理解链表能帮助我们更好地处理动态内存分配和频繁插入删除的场景。

一、Java 集合框架概览

Java 集合体系主要分为单列集合(Collection)和双列集合(Map)。

  • Collection:根接口,包含 List、Set、Queue 等子接口。
  • List:有序且可重复的列表,支持通过索引访问。
  • Map:存储键值对,Key 唯一。

本次重点聚焦于 LinkedList,它同时实现了 List 和 Deque 接口,兼具列表和双端队列的特性。

LinkedList 核心特性

  1. 结构:基于双向链表,节点包含前驱和后继引用,无固定容量限制。
  2. 访问效率:随机访问(get(i))需遍历,时间复杂度 O(n);相比数组实现的 ArrayList 较慢。
  3. 增删效率:在已知节点位置或两端进行增删操作效率高,时间复杂度 O(1)。
  4. 线程安全:非线程安全,多线程环境需外部同步。

关键接口解析

List 接口

定义了有序集合的基础行为,如 add(E e)、remove(int index)、get(int index) 等。由于底层是链表,索引访问必须从头或尾遍历,这是与 ArrayList 的核心区别。

Deque 接口

双端队列接口,允许在首尾进行插入、删除和获取。

  • 队列行为:先进先出(FIFO),如 offer()、poll()。
  • 栈行为:后进先出(LIFO),如 push()、pop()。
  • 优势:双向链表天然支持首尾 O(1) 操作,是 Deque 接口的优秀实现。
其他标记接口
  • Cloneable:支持浅拷贝,clone() 会创建新实例,元素引用共享。
  • Serializable:支持序列化,便于网络传输或持久化。
  • Iterable:提供迭代器遍历能力,支持增强 for 循环。

二、链表数据结构基础

链表由一系列节点组成,每个节点包含两部分:

  • 数据域:存储实际数据。
  • 指针域:指向下一个(或上一个)节点的引用。

常见的链表类型包括单向链表、双向链表,以及是否包含头节点的区别。手动实现时,通常从无头结点的单向链表入手,理解后再扩展至其他类型。

链表结构示意图

三、手动实现单向链表

为了深入理解机制,我们尝试手动实现一个基础的单向链表。代码分为节点定义、基本变量、核心方法三部分。

1. 节点定义

static class ListNode {
    int val;
    ListNode next;

    public ListNode(int val) {
        this.val = val;
    }
}

2. 成员变量与方法

我们需要维护一个头指针 head,并实现增删改查功能。

public class MyLinkedList {
    private ListNode head;

    // 初始化链表示例
    public ListNode createList() {
        ListNode node0 = new ListNode(1);
        ListNode node1 = new ListNode(2);
        ListNode node2 = new ListNode(3);
        ListNode node3 = new ListNode(4);
        ListNode node4 = new ListNode(5);
        
        node0.next = node1;
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        head = node0;
        return head;
    }

    // 头插法:O(1)
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        node.next = head;
        head = node;
    }

    // 尾插法:O(n)
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if (head == null) {
            head = node;
            return;
        }
        ListNode cur = head;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = node;
    }

    // 指定位置插入:O(n)
    public void addIndex(int index, int data) {
        if (index < 0 || index > size()) {
            System.out.println("index 不合法");
            return;
        }
        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        ListNode prev = findIndex(index - 1);
        ListNode node = new ListNode(data);
        node.next = prev.next;
        prev.next = node;
    }

    // 辅助方法:查找指定位置的前驱节点
    private ListNode findIndex(int index) {
        ListNode cur = head;
        int count = 0;
        while (count != index) {
            cur = cur.next;
            count++;
        }
        return cur;
    }

    // 查找是否存在某值
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    // 删除第一次出现的值为 key 的节点
    public void remove(int key) {
        if (head == null) {
            System.out.println("空链表异常");
            return;
        }
        if (head.val == key) {
            head = head.next;
            return;
        }
        ListNode prev = findIndex(key); // 这里简化逻辑,实际应查找前驱
        // 修正查找前驱逻辑
        ListNode cur = head;
        while (cur.next != null && cur.next.val != key) {
            cur = cur.next;
        }
        if (cur.next != null) {
            cur.next = cur.next.next;
        } else {
            System.out.println("未找到要删除的元素");
        }
    }

    // 获取链表长度
    public int size() {
        int count = 0;
        ListNode cur = head;
        while (cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }

    // 清空链表
    public void clear() {
        head = null;
    }

    // 打印链表
    public void display() {
        ListNode cur = head;
        while (cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }
}

四、官方 API 使用

Java 提供的 LinkedList 类封装了上述逻辑,并提供了更丰富的功能。

构造方法

  1. 无参构造:创建一个空的双向链表,first 和 last 指针均为 null。
  2. 带集合参数构造:将传入的 Collection 批量添加到链表中。
List<Integer> list1 = new LinkedList<>();
List<Integer> list2 = new ArrayList<>();
LinkedList<Integer> list3 = new LinkedList<>(list2);

常用操作

  • 添加:add(E e)、addFirst(E e)、addLast(E e)。
  • 删除:remove(Object o)、removeFirst()、removeLast()。
  • 获取:get(int index)、getFirst()、getLast()。
  • 判断:contains(Object o)、isEmpty()。

官方双向链表结构

注意区分不同方法的返回类型和形参,特别是涉及首尾操作的 Deque 方法。

五、遍历方式

1. 直接打印

利用 toString() 方法,输出所有元素。

System.out.println(list);

2. 索引循环

适用于需要下标访问的场景,但注意 get(i) 在 LinkedList 中是 O(n)。

for (int i = 0; i < list.size(); i++) {
    System.out.print(list.get(i) + " ");
}

3. 增强 For 循环

底层依赖迭代器,语法简洁。

for (Integer x : list) {
    System.out.print(x + " ");
}

4. 迭代器

最通用的遍历方式,支持正向和反向遍历。

// 正向
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.print(iterator.next() + " ");
}

// 反向
ListIterator<Integer> listIterator = list.listIterator(list.size());
while (listIterator.hasPrevious()) {
    System.out.print(listIterator.previous() + " ");
}

总结

通过对比手动实现与官方 API,我们可以清晰看到 LinkedList 在内存布局和操作效率上的特点。它在频繁插入删除的场景下表现优异,但在随机访问上不如数组。理解其底层双向链表结构及接口设计,有助于在实际开发中做出更合理的数据结构选择。

目录

  1. 概述
  2. 一、Java 集合框架概览
  3. LinkedList 核心特性
  4. 关键接口解析
  5. List 接口
  6. Deque 接口
  7. 其他标记接口
  8. 二、链表数据结构基础
  9. 三、手动实现单向链表
  10. 1. 节点定义
  11. 2. 成员变量与方法
  12. 四、官方 API 使用
  13. 构造方法
  14. 常用操作
  15. 五、遍历方式
  16. 1. 直接打印
  17. 2. 索引循环
  18. 3. 增强 For 循环
  19. 4. 迭代器
  20. 总结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 16 款 AI Agent 框架选型指南与实战对比
  • 基于 Sentry 自建前端错误监控系统实战
  • LangChain 框架快速入门指南
  • OpenClaw Dashboard 无法登录:systemd 缺失时的网关启动方案
  • 贝佐斯比尔盖茨英伟达等押注,NASA 工程师打造通用机器人大脑,估值 20 亿美元
  • Claude-Code 2.1.88 源码结构解析:从 Source Map 揭秘 AI 编程助手内部实现
  • AI 扩展定律背后的神话与真相
  • Docker 镜像国内拉取加速方案:使用渡渡鸟镜像站
  • Venera 开源漫画阅读器使用指南与优化技巧
  • 2024 年常用网络资源镜像站实测与使用指南
  • 6 层高速 PCB 设计实战:立创逻辑派 FPGA-G1 开发板笔记
  • 程序员进阶指南:精选在线学习、兼职与交流平台汇总
  • 用 Anthropic Skill 优化大模型前端设计的审美
  • Python 语法基础与入门指南
  • Vue3 开发:JavaScript 与 TypeScript 选型对比
  • 解决 IDEA 运行 Maven 时出现的 JAVA_TOOL_OPTIONS 编码警告
  • MIT 电机模式控制详解:参数、场景与调试
  • 前端国际化实现指南:多语言支持方案
  • JavaAI 全流程实操指南:从需求到部署的智能开发体验
  • 深入理解闪存磨损均衡算法

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online