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"); // 添加元素
}
相关推荐
matlab_xiaowang17 小时前
Redux 入门:JavaScript 可预测状态管理库
开发语言·javascript·其他·ecmascript
虹科网络安全17 小时前
艾体宝干货|数据复制详解:类型、原理与适用场景
java·开发语言·数据库
axng pmje17 小时前
Java语法进阶
java·开发语言·jvm
rKWP8gKv717 小时前
Java微服务性能监控:Prometheus与Grafana集成方案
java·微服务·prometheus
老前端的功夫17 小时前
【Java从入门到入土】28:Stream API:告别for循环的新时代
java·开发语言·python
qq_4352879217 小时前
第9章 夸父逐日与后羿射日:死循环与进程终止?十个太阳同时值班的并行冲突
java·开发语言·git·死循环·进程终止·并行冲突·夸父逐日
小江的记录本18 小时前
【Kafka核心】架构模型:Producer、Broker、Consumer、Consumer Group、Topic、Partition、Replica
java·数据库·分布式·后端·搜索引擎·架构·kafka
止语Lab18 小时前
从手动到框架:Go DI 演进的三个拐点
开发语言·后端·golang
yaoxin52112318 小时前
397. Java 文件操作基础 - 创建常规文件与临时文件
java·开发语言·python
小短腿的代码世界18 小时前
Qt日志系统深度解析:从qDebug到企业级日志框架
开发语言·qt