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

链表细节与 Java LinkedList 实战

通过手动实现单链表并分析 Java LinkedList 的双向链表特性,介绍了 LinkedList 的增删查改操作和遍历方式的效率差异。

不羁发布于 2026/6/302 浏览
链表细节与 Java LinkedList 实战

链表作为基本的线性表结构,在 Java 集合里对应 LinkedList。虽然实际开发中很少自己写链表,但明白它的工作原理会让你在选用集合时更有把握。

LinkedList 实现了 List 和 Deque 接口,底层是双向链表。这意味着它没有容量限制,在两端或节点附近做增删很快,但随机访问很慢——每次 get(index) 都要从头部或尾部遍历过去。

链表结构

每个节点(Node)包含数据域和前后指针。单链表只有一个方向,双向链表前后都能走。LinkedList 用的就是双向链表,可以从前或从后遍历。

图:单向链表结构示意图

手动实现单链表

我习惯先写一个简单的单链表,这样能切身体会那些操作的复杂度。下面是一个无头结点的单链表,实现一些基本操作。

节点定义与初始化

static class ListNode {
    int val;
    ListNode next;

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

public class MyLinkedList {
    private ListNode head;

    // 构造一个示例链表:1 -> 2 -> 3 -> 4 -> 5
    public ListNode createList() {
        ListNode node0 = new ListNode(1);
        ListNode node1 = new ListNode(2);
        ListNode node2 = new ListNode(3);
        ListNode node3 = new ListNode(4);
            ();
        
        node0.next = node1;
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        
        head = node0;
         head;
    }
ListNode
node4
=
new
ListNode
5
return

头插与尾插

头插法直接在 head 前插入,时间复杂度 O(1)。尾插法需要找到最后一个节点,所以是 O(n)。

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

// 尾插法
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;
}

任意位置插入

先找到前驱节点,然后调整指针。注意索引越界的情况。

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 = findPrev(index);
    ListNode node = new ListNode(data);
    node.next = prev.next;
    prev.next = node;
}

private ListNode findPrev(int index) {
    ListNode cur = head;
    int count = 0;
    while (count != index - 1) {
        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) return;
    if (head.val == key) {
        head = head.next;
        return;
    }
    ListNode prev = search(key);
    if (prev == null) return;
    ListNode del = prev.next;
    prev.next = del.next;
}

private ListNode search(int val) {
    ListNode cur = head;
    while (cur.next != null) {
        if (cur.next.val == val) return cur;
        cur = cur.next;
    }
    return null;
}

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

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

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

Java 中的 LinkedList

JDK 提供的 LinkedList 不仅作为列表,还是个双端队列。构造方法有无参和从其他集合构造两种。

List<Integer> list1 = new LinkedList<>();
List<Integer> list2 = new ArrayList<>();
LinkedList<Integer> list3 = new LinkedList<>(list2);

图:双向链表内部结构示意

常用的队列和栈操作:

  • addFirst / removeFirst:队首操作
  • addLast / removeLast:队尾操作
  • push / pop:模拟栈(等价于 addFirst/removeFirst)

遍历方式的差异

遍历 LinkedList 时,方式选不对会严重影响性能。

System.out.println(list); 直接用 toString 输出,适合调试。

通过索引的 for 循环 for (int i = 0; i < list.size(); i++) 在 LinkedList 上是灾难——每次 get(i) 都需要从链表头部开始找,复杂度 O(n²)。数据量稍大就会慢得离谱。

迭代器是更好的选择,它内部维护了当前节点的引用,每次 next 只是向前走一步。

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

如果要从后往前遍历,可以使用 ListIterator:

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

理解了这些底层的实现,才能在合适的场景下挑对集合。例如频繁随机访问就该用 ArrayList,如果需要频繁在头部插入或删除,LinkedList 会更合适。

目录

  1. 链表结构
  2. 手动实现单链表
  3. 节点定义与初始化
  4. 头插与尾插
  5. 任意位置插入
  6. 查找与删除
  7. Java 中的 LinkedList
  8. 遍历方式的差异
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • 用 WebDAV 和 RaiDrive 把绿联 NAS 挂载成本地磁盘
  • Qwen3-VL 的双模式是怎么工作的?Instruct 与 Thinking 实践对比
  • C++ 七大排序算法详解
  • CVE-2015-7450:WebSphere 反序列化漏洞的攻击链分析
  • 10 个 Python 脚本让日常重复活自动跑起来
  • 被工具定义的编程时代:VS Code 与 JetBrains 效率提升实战指南
  • C++ 核心就业方向与技术成长指南
  • 前端防录屏原理:EME 与 DRM 机制及实战代码
  • Agentic AI 核心概念及与传统 AIGC 的区别解析
  • 2025 主流 AI IDE 对比:Trae、Copilot、Windsurf、Cursor 选型指南
  • spdlog 日志库嵌入式 Linux C++使用指南
  • OpenClaw Docker 部署指南:集成飞书钉钉 QQ 企业微信机器人
  • Claude Code 源码因 Source Map 配置失误泄露,51 万行代码公开
  • 麒麟系统 TongWeb 8 安装部署指南
  • ChatGPT 核心功能与高级使用技巧指南
  • 5 款 Stable Diffusion 云端协作平台推荐
  • VSCode Copilot 在 Win10 WSL2 环境无法使用的问题
  • 2023 信奥赛 C++ 提高组 CSP-S 复赛真题:密码锁
  • 本地部署 OCR 文字检测系统:ResNet18 WebUI 一键启动指南
  • VSCode 中 GitHub Copilot 安装与实战指南

相关免费在线工具

  • 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