【Java】关于容器选型:Deque、PriorityQueue、LinkedList

一次把栈、队列、双端队列、优先队列、链表讲清楚 ------ 从"能不能用 Deque 全包"到"实际该怎么选"。


一、起因:Deque 是不是万能容器?

问题 :Java 里双端队列 Deque 的功能是不是完全囊括了普通栈和队列?实际开发能不能全用 Deque?

结论:

  • 功能上 :Deque 确实是栈和队列的超集,理论上可以只用它。
  • 实践上 :不建议无脑全用,要按语义选接口。
  • 例外 :优先队列、阻塞队列、线程安全队列等,Deque 替代不了。

一句话:Deque 是"能",但不是"该"。工具选最贴合语义的那个。


二、ArrayDeque 常用方法

ArrayDeque 是 Deque 接口最常用的实现,底层是可扩容循环数组 ,非线程安全,作为栈/队列时性能优于 Stack 和 LinkedList。

两端命名

复制代码
        addFirst / offerFirst          addLast / offerLast
              ↓                              ↓
         [ head ]  ←  ... 循环数组 ...  →  [ tail ]
              ↑                              ↑
        removeFirst / pollFirst        removeLast / pollLast
              peekFirst                      peekLast

方法分组

功能 头部 尾部
插入 addFirst / offerFirst addLast / offerLast
删除 removeFirst / pollFirst removeLast / pollLast
查看 getFirst / peekFirst getLast / peekLast

抛异常 vs 返回特殊值(口诀)

  • add / remove / get → 失败抛异常
  • offer / poll / peek → 失败返回特殊值 (false / null)

快捷方法

角色 方法
栈 push / pop / peek
队列 offer / poll / peek

注意事项

  1. 不能存 null ,会抛 NullPointerException。
  2. 非线程安全 ,多线程用 ConcurrentLinkedDeque。
  3. 随机访问 O(n),没有下标 O(1) 访问。
  4. 作为栈/队列,比 Stack(同步开销)和 LinkedList(缓存不友好)都快。

三、为什么有两种声明写法?------ 面向接口编程

java 复制代码
Deque<Integer> stack = new ArrayDeque<>();        // ✅ 推荐
ArrayDeque<Integer> stack = new ArrayDeque<>();   // 也能跑,但少见
  • 左边 = 引用类型(编译期):决定能调用哪些方法。
  • 右边 = 对象类型(运行期):决定实际行为。

编译看左边,运行看右边。

为什么推荐"接口在左,实现在右"

  1. 解耦:换实现只改右边一处。
  2. 缩小暴露面:只暴露必要方法,减少误用。
  3. 表达意图:一眼看出变量扮演什么角色。
  4. 便于测试/依赖倒置:方法参数用接口,可传任意实现。

什么时候用具体类声明

  • 需要该类接口之外的特有方法 (如 ArrayList.ensureCapacity)。
  • 局部变量、生命周期极短、不涉及替换。
  • 对 ArrayDeque 来说几乎总是用 Deque 声明。

四、优先队列 PriorityQueue

声明

java 复制代码
Queue<Integer> pq = new PriorityQueue<>();  // ✅ 推荐

不能 写成 Deque<Integer> pq = ...,因为 PriorityQueue 只实现 Queue,没实现 Deque。

为什么 Deque 替代不了它

容器 出队规则
普通队列 先进先出(FIFO)
双端队列 两端进出,按位置
优先队列 按优先级,与入队顺序无关

Deque 的 offerFirst/pollLast 对优先队列毫无意义------元素位置由优先级决定。

常用方法

方法 说明
offer / add 入队
poll / remove 出队(返回优先级最高元素)
peek / element 查看队首

关键 :poll/peek 拿到的是优先级最高 的元素,不是最早入队的;不允许 null;非线程安全。

默认小顶堆

java 复制代码
Queue<Integer> pq = new PriorityQueue<>();  // 默认小顶堆

自定义优先级:

java 复制代码
Queue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);        // 大顶堆
Queue<String> byLen = new PriorityQueue<>(Comparator.comparingInt(String::length));

典型场景

  • 第 K 大 / 第 K 小、Top K 高频
  • Dijkstra 最短路
  • 合并 K 个有序链表
  • 数据流中位数(对顶堆)

五、LinkedList 详解

继承体系

java 复制代码
public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, Serializable

同时实现 List 和 Deque,是"多面手":

java 复制代码
List<Integer> list = new LinkedList<>();    // 当 List
Deque<Integer> deque = new LinkedList<>();  // 当栈/队列
Queue<Integer> queue = new LinkedList<>();  // 当队列

底层结构

双向链表,每个节点存 prev、item、next,额外维护 first / last 引用。没有下标数组。

性能特征

操作 LinkedList ArrayList ArrayDeque
尾部插入 O(1) 均摊 O(1) 均摊 O(1)
头部插入/删除 O(1) O(n) O(1)
按下标 get(i) O(n) O(1) O(n)
内存开销 大 小 小
缓存友好性 差 好 好

LinkedList vs ArrayDeque

维度 LinkedList ArrayDeque
底层 双向链表 循环数组
栈/队列性能 较慢 更快
内存 大 小
存 null 允许 不允许
实现 List ✅ ❌

结论 :当栈/队列优先 ArrayDeque;需存 null 才用 LinkedList。

常见误区

  • ❌ 插入删除总是 O(1):add(index,e) 仍要 O(n) 找位置。
  • ❌ LinkedList 比 ArrayList 快:大多数场景 ArrayList 更快(缓存友好)。
  • ❌ 当栈/队列用 LinkedList 没问题:能跑,但 ArrayDeque 更快。
  • ❌ for (int i=0;i<n;i++) list.get(i):O(n²) 陷阱,用 for-each。

六、三种容器声明对照表

容器 推荐声明 实现 声明为 Deque?
栈 Deque<T> new ArrayDeque<>() ✅ 可以
队列 Queue<T> new ArrayDeque<>() ✅ 可以
优先队列 Queue<T> new PriorityQueue<>() ❌ 不行
列表 List<T> new ArrayList<>() ---

规律:

  • 能用哪个接口声明,取决于右边实现类实现了哪些接口。
  • 左边接口决定"能调什么",右边实现决定"实际怎么做"。

七、最终选型口诀

需求 选择
列表 ArrayList
栈 / 队列 / 双端队列 ArrayDeque
优先级 PriorityQueue
需要存 null / 频繁头插 LinkedList
线程安全 ConcurrentLinkedDeque、PriorityBlockingQueue 等

核心原则:

  1. 接口在左,实现在右(面向接口编程)。
  2. 按语义选容器,而不是"哪个万能用哪个"。
  3. Deque 是栈和队列的超集,但不是优先队列的替代品。
  4. 默认选 ArrayDeque / ArrayList ,LinkedList 只在有特殊需求时用。

八、一句话收尾

Deque 一个接口,栈、队列、双端队列三种角色全包;PriorityQueue 管优先级;LinkedList 是多面手但常被误用。能用一个容器做多件事,不代表应该------代码是给人看的,语义清晰比"省一个类"重要得多。


本文整理自一次关于 Java 容器选型的讨论,涵盖 Deque、ArrayDeque、PriorityQueue、LinkedList 的对比与实战建议。

相关推荐
Rain的Java大神之路1 小时前
🔥13年Java老兵转型AI Agent:90%的人挂在同一个坑,根本不用学Python!(万字实战复盘,建议收藏)
java·后端·面试
扶风ff1 小时前
练题簿在线免费刷题:创建个人任务,把刷题、听题和模考目标放到每天的学习中
开发语言·javascript·学习·小程序
yuniko-n1 小时前
【JUC】wait 和 sleep
java·开发语言
知守观1 小时前
18年老兵的十条开发军规:每条背后都是一个翻车现场
java·后端·程序员
kimnoic1 小时前
Python常见模块及其用法示例详解
开发语言·python
BBmmo1 小时前
我的 Java 学习笔记 · 第 11 篇:Condition.await 为什么必须放在 while 里
java
SimonKing1 小时前
一只离线鼠鼠,干翻了一堆在线格式转换网站
java·后端·程序员
程序员-Benothing1 小时前
Linux文本排序统计命令:sort、uniq、wc、cut、tr实战
java·开发语言·算法
clz13145211 小时前
双重检查锁 DCL 终极优化:懒加载 + 线程安全 + 高性能
java