Java:LinkedList的使用

目录

一、概念

二、常用操作

[2.1 基于List的操作](#2.1 基于List的操作)

[2.2 队列(Queue)、栈(Stack)和双端队列(Deque)操作](#2.2 队列(Queue)、栈(Stack)和双端队列(Deque)操作)

[2.3 遍历](#2.3 遍历)


一、概念

LinkedList 在 Java 中是一个基于双向链表 实现的 List 接口的实现类。它同时实现了 ListDeque(双端队列)接口,所以它同时具备了列表和队列的特性。

核心特点:

  • 非连续存储:元素分散存储在堆内存中,通过引用相连。这使其在内存利用上更灵活,但每个元素需要额外的空间来存储前后节点的引用(内存开销更大)。

  • 动态大小:与数组不同,链表的大小是动态的,可以自由地添加和删除元素,无需担心容量问题。

  • 插入和删除高效在已知位置(通过迭代器定位)进行插入和删除操作的时间复杂度是 O(1) 。这是它相对于 ArrayList 最大的优势。因为它只需要修改相邻节点的引用即可,而不需要像数组那样移动大量元素。

  • 随机访问低效根据索引访问元素(get(i), set(i, element))的时间复杂度是 O(n)。因为它必须从链表的头部(或尾部,JDK 做了优化)开始遍历,直到找到第 i 个节点。

  • 实现了双端队列(Deque) :提供了丰富的方法,如 addFirst(), addLast(), removeFirst(), removeLast(), getFirst(), getLast() 等,可以非常方便地将其用作栈(Stack)、队列(Queue)或双端队列(Deque)。

二、常用操作

2.1 基于List的操作

java 复制代码
LinkedList<String> list = new LinkedList<>();

// 添加元素
list.add("Apple");        // 添加到尾部
list.add(1, "Banana");   // 在指定索引位置插入
list.addFirst("First");   // 添加到头部 (Deque 方法)
list.addLast("Last");     // 添加到尾部 (Deque 方法,等同于 add())

// 获取元素
String first = list.get(0);     // 根据索引获取
String head = list.getFirst();  // 获取头部元素
String tail = list.getLast();   // 获取尾部元素

// 删除元素
list.remove(1);           // 根据索引删除
list.remove("Apple");     // 根据元素内容删除(首次出现)
list.removeFirst();       // 删除并返回头部元素
list.removeLast();        // 删除并返回尾部元素

// 检查大小和内容
int size = list.size();
boolean isEmpty = list.isEmpty();
boolean hasApple = list.contains("Apple");

// 替换元素
list.set(0, "New First"); // 将索引0位置的元素替换

2.2 队列(Queue)、栈(Stack)和双端队列(Deque)操作

java 复制代码
LinkedList<String> queue = new LinkedList<>();

// 作为普通队列 (FIFO: First-In-First-Out)
queue.offer("A"); // 入队(添加到尾部),推荐使用
queue.add("B");   // 入队(添加到尾部),失败会抛异常
String s1 = queue.poll(); // 出队(移除并返回头部元素),队列空则返回null
String s2 = queue.remove(); // 出队,队列空则抛异常 NoSuchElementException
String s3 = queue.peek(); // 获取但不移除头部元素,空则返回null
String s4 = queue.element(); // 获取但不移除头部元素,空则抛异常

// 作为栈 (LIFO: Last-In-First-Out)
LinkedList<String> stack = new LinkedList<>();
stack.push("A"); // 压栈(添加到头部)
stack.push("B");
String top = stack.pop(); // 出栈(移除并返回头部元素)
String top2 = stack.peek(); // 查看栈顶元素

// 作为双端队列 (Deque)
queue.offerFirst("A"); // 添加到头部
queue.offerLast("Z");  // 添加到尾部(等同于 offer)
String first = queue.pollFirst(); // 从头部移除
String last = queue.pollLast();   // 从尾部移除

典型使用场景:

  • 实现栈(LIFO)addFirst() / removeFirst()

  • 实现队列(FIFO)offer() / poll()addLast() / removeFirst()

  • 实现双端队列 :各种 First/Last 方法

操作 方法示例 时间复杂度 说明
头部插入 addFirst(e), offerFirst(e) O(1)
尾部插入 add(e), addLast(e), offer(e), offerLast(e) O(1)
指定位置插入 add(index, e) O(n) 主要耗时在遍历到 index 位置
头部删除 removeFirst(), pollFirst() O(1)
尾部删除 removeLast(), pollLast() O(1)
指定元素/位置删除 remove(o), remove(index) O(n) 主要耗时在遍历找到元素或位置
随机访问(获取) get(index) O(n) 需要遍历
随机访问(修改) set(index, e) O(n) 需要遍历到位置后再修改,修改本身是 O(1)
查找元素位置 indexOf(o) O(n) 需要遍历比较
检查是否包含 contains(o) O(n) 内部调用 indexOf

队列使用示例:

java 复制代码
import java.util.LinkedList;
import java.util.Queue;

public class QueueExample {
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();

        // 生产者:入队10个任务
        for (int i = 0; i < 10; i++) {
            queue.offer(i);
            System.out.println("任务入队: " + i);
        }

        // 消费者:处理所有任务
        while (!queue.isEmpty()) {
            Integer task = queue.poll();
            System.out.println("处理任务: " + task);
        }
    }
}

2.3 遍历

千万不要用 for-i 循环(基于索引的循环)来遍历 LinkedList

因为每次 get(i) 都会从链表头开始遍历,会导致时间复杂度变为 O(n²),性能极差。

java 复制代码
// foreach 循环(推荐):语法简洁,底层使用迭代器,效率高。
for (String item : list) {
    System.out.println(item);
}


// 迭代器(Iterator):更灵活,可以在遍历时使用 iterator.remove() 安全地删除元素。
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String item = iterator.next();
    if ("某些条件") {
        iterator.remove(); // 安全删除!
    }
}


// ListIterator:功能更强大的迭代器,可以双向遍历(向前/向后)以及在遍历时添加和修改元素。
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {
    String next = listIterator.next();
    listIterator.set("Modified: " + next); // 修改元素
    listIterator.add("Added"); // 添加元素
}
相关推荐
vker11 分钟前
第 2 天:工厂方法模式(Factory Method Pattern)—— 创建型模式
java·后端·设计模式
准时睡觉13 分钟前
SpringSecurity的使用
java·后端
对不起初见21 分钟前
如何在后端优雅地生成并传递动态错误提示?
java·spring boot
tingyu22 分钟前
JAXB 版本冲突踩坑记:SPI 项目中的 XML 处理方案升级
java
NightDW27 分钟前
amqp-client源码解析1:数据格式
java·后端·rabbitmq
程序员清风1 小时前
美团二面:KAFKA能保证顺序读顺序写吗?
java·后端·面试
ytadpole15 小时前
揭秘xxl-job:从高可用到调度一致性
java·后端
玉衡子16 小时前
六、深入理解JVM执行引擎
java·jvm
每天进步一点_JL16 小时前
JVM 内存调优:到底在调什么?怎么调?
java·jvm·后端
yinke小琪16 小时前
说说Java 中 Object 类的常用的几个方法?详细的讲解一下
java·后端·面试