Java 实例 – 在链表(LinkedList)的开头和结尾添加元素(长文讲解)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观

在 Java 编程中,链表(LinkedList) 是一种灵活且高效的数据结构,它允许开发者在动态环境中高效地管理数据。无论是构建复杂的算法还是实现日常的业务逻辑,掌握如何在链表的开头和结尾快速添加元素,都是提升代码效率和可维护性的关键技能。本文将通过实例解析、代码演示和性能分析,深入探讨这一主题,并帮助读者理解其背后的原理与应用场景。


一、链表(LinkedList)基础概念

1.1 什么是链表?

链表是一种由 节点(Node) 组成的线性数据结构,每个节点包含两个部分:

  1. 数据域(Data):存储实际数据;
  2. 指针域(Next):指向下一个节点的引用。

可以将链表想象成 火车车厢的连接方式:每一节车厢(节点)通过挂钩(指针)连接到下一节,而整个链表的开头(头节点)和结尾(尾节点)则决定了数据的存取路径。

1.2 Java 中的 LinkedList 类

Java 标准库提供了 java.util.LinkedList 类,它实现了 List 接口,支持以下特性:

  • 动态大小:无需预先定义容量,元素数量可随需增减;
  • 快速插入与删除:在链表的任意位置插入或删除元素的时间复杂度为 O(1)(前提是已知目标节点的位置);
  • 双端队列支持:可以通过 addFirst()addLast() 等方法高效操作两端元素。

二、在链表的开头添加元素

2.1 方法实现与原理

要在链表的 开头添加元素,可以使用以下两种方法:

  1. addFirst(E e):直接将元素插入到链表头部;
  2. add(0, e):通过指定索引 0 将元素插入到开头。

2.1.1 代码示例

import java.util.LinkedList;

public class LinkedListExample {
    public static void main(String[] args) {
        // 创建一个空链表
        LinkedList<String> linkedList = new LinkedList<>();

        // 方法一:使用 addFirst()
        linkedList.addFirst("元素1");

        // 方法二:使用 add(0, e)
        linkedList.add(0, "元素0");

        System.out.println("链表内容:" + linkedList);
        // 输出:[元素0, 元素1]
    }
}

2.1.2 原理分析

  • addFirst()

    • 直接将新节点的 next 指针指向原头节点,然后更新头节点为新节点。
    • 时间复杂度为 O(1),无需遍历链表。
  • add(0, e)

    • 通过索引操作,本质上也是调用 addFirst(),但需要额外的索引校验,性能略低。

2.2 场景应用

链表头部插入操作常用于实现 栈(Stack)优先队列。例如,模拟一个先进后出的队列时,可以通过 addFirst() 快速插入元素。


三、在链表的结尾添加元素

3.1 方法实现与原理

要在链表的 结尾添加元素,可以使用以下方法:

  1. addLast(E e):直接将元素插入到链表尾部;
  2. add(E e)add(size(), e):通过默认方法或指定索引将元素添加到末尾。

3.1.1 代码示例

import java.util.LinkedList;

public class LinkedListExample {
    public static void main(String[] args) {
        LinkedList<String> linkedList = new LinkedList<>();

        // 方法一:使用 addLast()
        linkedList.addLast("元素A");

        // 方法二:使用 add(E e)
        linkedList.add("元素B");

        // 方法三:使用 add(size(), e)
        linkedList.add(linkedList.size(), "元素C");

        System.out.println("链表内容:" + linkedList);
        // 输出:[元素A, 元素B, 元素C]
    }
}

3.1.2 原理分析

  • addLast()

    • 直接将新节点的 next 指针设为 null,并更新尾节点的 next 指向新节点。
    • 时间复杂度为 O(1)
  • add(E e)

    • 等价于 addLast(),但需确保链表未被修改为其他结构(如双端队列)。
  • add(size(), e)

    • 通过索引操作,需遍历链表到尾部,时间复杂度为 O(n),因此不推荐频繁使用。

3.2 场景应用

链表尾部插入操作适用于 队列(Queue) 或需要按顺序追加数据的场景,例如日志记录或任务队列。


四、LinkedList 与 ArrayList 的对比

4.1 插入与删除性能

操作类型LinkedList 时间复杂度ArrayList 时间复杂度
在开头插入元素O(1)O(n)
在结尾插入元素O(1)O(1)(平均)
在中间插入元素O(n)O(n)

4.1.1 对比分析

  • 插入/删除操作

    • LinkedList 在头部或尾部操作时效率极高,适合频繁增删的场景;
    • ArrayList 在尾部插入快,但头部或中间插入需要移动元素,效率较低。
  • 随机访问

    • ArrayList 支持 O(1) 的随机访问(通过索引直接定位);
    • LinkedList 需要遍历链表到目标节点,时间复杂度为 O(n)

4.2 使用场景建议

  • 选择 LinkedList:当需要频繁在头部或中间进行增删操作时(如栈、队列、动态列表);
  • 选择 ArrayList:当需要大量随机访问元素或追加元素到尾部时(如固定大小的列表)。

五、实际案例与代码演示

5.1 案例:实现一个简单队列

import java.util.LinkedList;

public class SimpleQueue {
    private LinkedList<String> queue;

    public SimpleQueue() {
        queue = new LinkedList<>();
    }

    // 入队操作(尾部添加)
    public void enqueue(String item) {
        queue.addLast(item);
    }

    // 出队操作(头部移除)
    public String dequeue() {
        if (queue.isEmpty()) {
            throw new RuntimeException("队列为空!");
        }
        return queue.removeFirst();
    }

    public static void main(String[] args) {
        SimpleQueue q = new SimpleQueue();
        q.enqueue("任务1");
        q.enqueue("任务2");
        System.out.println("出队结果:" + q.dequeue()); // 输出:任务1
    }
}

5.2 案例:动态更新列表

import java.util.LinkedList;

public class DynamicList {
    public static void main(String[] args) {
        LinkedList<Integer> numbers = new LinkedList<>();

        // 添加元素到开头
        numbers.addFirst(10);
        numbers.addFirst(20); // 现在列表为 [20, 10]

        // 添加元素到结尾
        numbers.addLast(30); // 现在列表为 [20, 10, 30]

        // 插入元素到中间
        numbers.add(1, 15); // 现在列表为 [20, 15, 10, 30]

        System.out.println("最终列表:" + numbers);
    }
}

六、常见问题与解决方案

6.1 问题:如何避免链表操作中的 IndexOutOfBoundsException

解答

  • 在插入或删除元素前,先检查链表是否为空;
  • 使用 size() 方法获取链表长度,确保索引在有效范围内。

示例代码:

if (!linkedList.isEmpty()) {
    linkedList.removeFirst();
}

6.2 问题:为什么 add(0, e) 的性能比 addFirst() 差?

解答

  • add(0, e) 需要校验索引是否合法,而 addFirst() 是直接操作头节点,无额外校验。

七、结论

通过本文的讲解,我们深入理解了 Java 实例 – 在链表(LinkedList)的开头和结尾添加元素 的实现方法、性能特性及实际应用场景。链表作为一种灵活的数据结构,其高效增删的特点使其在动态数据管理中发挥重要作用。开发者在选择数据结构时,需结合具体需求权衡性能与功能,例如:

  • 若需频繁操作两端元素,优先使用 LinkedList
  • 若需频繁随机访问元素,优先使用 ArrayList

掌握这些基础知识后,读者可以进一步探索链表的其他高级操作(如反转、合并、排序等),并将其应用于更复杂的算法和系统设计中。

最新发布