深入理解三大高频队列:ArrayDeque、LinkedList、PriorityQueue 方法、特性与场景选择

在 Java 日常开发和算法刷题中,ArrayDequeLinkedListPriorityQueue 是三个出场率极高的容器。它们都实现了 Queue 接口,但底层结构、支持的操作和适用场景却截然不同。本文将从方法 API、底层特性、横向对比到代码实战,为你彻底梳理这三个集合类的核心知识。


一、ArrayDeque ------ 循环数组实现的双端队列

官方推荐: 可用作 FIFO 队列、LIFO 栈、双端队列,禁止存放 null 元素

ArrayDeque 底层由循环数组实现,头尾操作均摊时间复杂度均为 O(1) ,性能优于 LinkedList。它是 Deque 接口的主要实现类,在需要队列或栈的场景中应优先选用。

1.1 双端队列常用 API

方法 作用 失败行为
addFirst(e) 头部添加元素 容量满时抛异常
addLast(e) / add(e) 尾部添加元素 容量满时抛异常
offerFirst(e) 头部添加元素 返回 true / false
offerLast(e) / offer(e) 尾部添加元素 返回 true / false
removeFirst() 删除并返回头部元素 空队列抛异常
removeLast() 删除并返回尾部元素 空队列抛异常
pollFirst() 删除并返回头部元素 空队列返回 null
pollLast() 删除并返回尾部元素 空队列返回 null
getFirst() 查看头部元素(不删除) 空队列抛异常
getLast() 查看尾部元素(不删除) 空队列抛异常
peekFirst() 查看头部元素(不删除) 空队列返回 null
peekLast() 查看尾部元素(不删除) 空队列返回 null

1.2 作为栈使用(官方推荐替代 Stack

  • push(e) → 等价于 addFirst(e) ------ 入栈
  • pop() → 等价于 removeFirst() ------ 出栈
  • peek() → 等价于 peekFirst() ------ 查看栈顶

1.3 关键特性

  • 底层循环数组,自动扩容,头尾操作极快。
  • 不支持按索引随机访问 (没有 get(index) 方法)。
  • 非线程安全,多线程并发需自行同步。
  • 不允许存入 null 元素。

1.4 代码示例

复制代码
import java.util.ArrayDeque;

public class ArrayDequeDemo {
    public static void main(String[] args) {
        ArrayDeque<Integer> deque = new ArrayDeque<>();

        // 1. 当作普通 FIFO 队列(先进先出)
        deque.offer(1);               // 队尾入队
        deque.offer(2);
        Integer head = deque.poll();  // 队头出队,空返回 null
        Integer peek  = deque.peek(); // 查看队头,不删除

        // 2. 当作栈使用(后进先出,推荐替代 Stack)
        deque.push(10);               // 入栈
        deque.push(20);
        Integer top = deque.pop();    // 弹出栈顶,空抛异常
        Integer stackTop = deque.peek(); // 查看栈顶

        // 3. 双端队列:头尾自由操作(滑动窗口、单调队列核心)
        deque.offerFirst(100);        // 头部添加
        deque.offerLast(200);         // 尾部添加
        deque.pollFirst();            // 头部删除
        deque.pollLast();             // 尾部删除
    }
}

适用场景: BFS 队列、单调栈、滑动窗口单调队列、任何需要纯队列或栈的场合。


二、LinkedList ------ 双向链表,一身三用

允许存放 null 元素 ,同时实现了 ListDeque 接口,可用作列表、队列或双端队列。

LinkedList 底层为双向链表,在头尾节点的增删同样是 O(1),但由于需要维护额外节点对象,内存开销和缓存友好度均不如 ArrayDeque

2.1 List 接口方法(体现链表特性)

  • add(int index, E element) ------ 指定位置插入
  • get(int index) ------ 按索引获取元素,时间复杂度 O(n)
  • remove(int index) ------ 按索引删除

⚠️ 由于 get(i) 需要遍历,不适合大量随机访问。

2.2 Deque / Queue 全套方法

LinkedList 实现了与 ArrayDeque 完全相同的方法签名:addFirstaddLastofferFirstofferLastremoveFirstpollFirstpeekFirstpushpoppeek 等等。

2.3 优缺点

  • ✅ 优势:在 已经定位到节点 的前提下,中间插入/删除只涉及指针更改。
  • ❌ 劣势:每个元素包装成 Node 对象,内存占用大,CPU 缓存不友好;纯头尾操作性能弱于 ArrayDeque

2.4 代码示例

复制代码
import java.util.LinkedList;

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

        // 1. 当作 List 使用(支持索引,但 O(n))
        list.add(1);
        list.add(1, 5);   // 在索引 1 处插入
        int num = list.get(0);  // 随机访问

        // 2. 当作 FIFO 队列
        list.offer(10);
        list.poll();
        list.peek();

        // 3. 当作栈 / 双端队列(方法同 ArrayDeque)
        list.push(100);
        list.pop();
        list.offerFirst(20);
        list.pollLast();
    }
}

适用场景: 需要同时满足 List 索引访问 + 队列/栈操作,或者频繁在链表中间位置增删元素的场景。


三、PriorityQueue ------ 优先级队列(二叉堆实现)

仅实现 Queue 接口,不是双端队列! 不允许 null,元素必须可比较,默认 最小堆

PriorityQueue 底层基于数组实现的二叉堆,入队(offer)和出队(poll)的时间复杂度均为 O(log n)。它只能操作队首(堆顶),不能操作队尾。

3.1 核心 Queue 方法

方法 功能
offer(E e) 加入元素,自动上浮调整堆,O(log n)
poll() 取出并移除堆顶元素(优先级最高),下沉调整,O(log n);空返回 null
peek() 查看堆顶元素,O(1);空返回 null
add(E e) 等价 offer,失败抛异常
remove() 等价 poll,空队列抛异常
element() 等价 peek,空队列抛异常

3.2 重要坑点

  1. 没有 addFirstpollLast 这类双端方法!
  2. 直接迭代(for-each不保证有序 ,只有不断 poll() 才能按优先级顺序取出。
  3. 删除任意元素 remove(Object o) 需要 O(n) 遍历查找。
  4. 默认是最小堆,构造大根堆需传入 Comparator.reverseOrder()

3.3 代码示例

复制代码
import java.util.PriorityQueue;
import java.util.Comparator;

public class PriorityQueueDemo {
    public static void main(String[] args) {
        // 1. 默认最小堆:堆顶是最小值
        PriorityQueue<Integer> minHeap = new PriorityQueue<>();
        minHeap.offer(5);
        minHeap.offer(2);
        minHeap.offer(8);
        System.out.println(minHeap.peek()); // 输出 2
        minHeap.poll();                     // 移除堆顶

        // 2. 最大堆写法
        PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
        maxHeap.offer(3);
        maxHeap.offer(9);
        System.out.println(maxHeap.peek()); // 输出 9

        // ❌ 错误:没有 offerFirst 方法!
        // maxHeap.offerFirst(1);
    }
}

适用场景: TopK 问题、多路归并、任务优先级调度、动态数据流求中位数等。


四、通用方法设计模式(Queue 接口规范)

所有队列实现都遵循同一套两套方法约定,记忆一组即可:

操作 失败时抛异常 失败时返回特殊值(推荐)
插入 add(e) offer(e)
删除 remove() poll()
查看队首 element() peek()

五、横向关键对比

维度 ArrayDeque LinkedList PriorityQueue
底层结构 循环数组 双向链表 数组实现的二叉堆
实现接口 Deque List & Deque Queue
能否做栈 / 双端队列
头尾增删 O(1) 均摊,最快 O(1),但常数大 ---
插入 / 删除 --- --- O(log n)
是否允许 null
有序规则 按插入顺序 按插入顺序 按优先级(堆序)
随机索引访问 ✅ O(n)

六、场景选择速记口诀

  • 滑动窗口、单调队列、普通栈与队列ArrayDeque
  • 同时需要 List 索引访问和队列操作,或频繁中间插入删除LinkedList
  • 前 K 大/小、任务调度、多路归并PriorityQueue

七、总结

Queue 的设计十分灵活,一个 Queue 接口背后却对应着多种截然不同的实现。理解它们的底层数据结构和性能边界,是写出高效、优雅代码的基础。希望本文的 API 速查、代码示例和场景对比能帮助你快速选择适合的容器,在开发与面试中游刃有余。

相关推荐
roman_日积跬步-终至千里1 小时前
【资源控制】自助查询的智能路由
java·大数据·数据库
步行cgn1 小时前
MyBatis Error evaluating expression ‘ids‘. Return value (3) was not iterable 错误详
java·后端
lhldsg2 小时前
社区健身场地规划实战指南:从器材配置到智能化管理经验分享
java·开发语言·经验分享·小程序
LayZhangStrive2 小时前
后端通识 - 后端工程开发常用的流程图类别
java·流程图·后端开发
WIN赢2 小时前
【抽象思想-从复杂中抽离简单、收敛的口子】
java·前端·javascript
captain3762 小时前
多线程单例模式
java·单例模式·java-ee
xiaotianyuanma2 小时前
【计算机毕业设计】基于Spring Boot的体育馆预约系统的设计与实现
java·spring boot·课程设计
何以解忧,唯有..2 小时前
MySQL 索引分类与底层原理深度剖析
java
水无痕simon3 小时前
6、六大工作模式-2
java·rabbitmq