Java中Queue以及Deque详解

一、核心概念与继承体系

二、Queue 核心方法与实现

1. 核心操作:

方法 说明 异常处理
offer(e) 添加元素(推荐) 失败返回false
add(e) 添加元素 失败抛IllegalStateException
poll() 移除并返回队头元素 空队列返回null
remove() 移除并返回队头元素 空队列抛NoSuchElementException
peek() 查看队头元素(不删除) 空队列返回null
element() 查看队头元素(不删除) 空队列抛NoSuchElementException

2. 常用实现类:

  • LinkedList:基于链表,支持null元素

  • PriorityQueue:基于堆的优先级队列(自然序/Comparator)

  • ArrayBlockingQueue:固定大小的阻塞队列(线程安全)

  • LinkedBlockingQueue:可选有界阻塞队列(线程安全)


三、Deque 双端队列扩展

1. 核心操作增强:

java 复制代码
// 头部操作
offerFirst(e)  // 头部插入
pollFirst()    // 头部移除
peekFirst()    // 查看头部

// 尾部操作
offerLast(e)   // 尾部插入(等价于offer)
pollLast()     // 尾部移除
peekLast()     // 查看尾部

// 栈操作
push(e)        // = offerFirst(e)
pop()          // = removeFirst()

2. 作为队列使用(FIFO)的API:

java 复制代码
// 队列操作(FIFO:先进先出)
offerLast(e) 或 offer(e)  // 入队(尾部添加)
pollFirst() 或 poll()     // 出队(头部移除)
peekFirst() 或 peek()     // 查看队头

3. 作为栈使用(LIFO)的API:

java 复制代码
// 栈操作(LIFO:后进先出)
push(e)         // 入栈 = addFirst(e)
pop()           // 出栈 = removeFirst()
peekFirst()     // 查看栈顶

4. API使用对照表:

操作 队列模式(FIFO) 栈模式(LIFO) 等效方法
添加元素 offerLast(e) / offer(e) push(e) addFirst(e)(栈)
addLast(e)(队列)
移除元素 pollFirst() / poll() pop() removeFirst()
查看元素 peekFirst() / peek() peekFirst() getFirst()

5. 代码示例:

java 复制代码
Deque<String> deque = new ArrayDeque<>();

// 作为队列使用(FIFO)
deque.offerLast("A"); // 队尾添加
deque.offerLast("B");
System.out.println(deque.pollFirst()); // A(队头移除)

// 作为栈使用(LIFO)
deque.push("C");      // 入栈
deque.push("D");
System.out.println(deque.pop());      // D(出栈)

// 混合操作(不推荐但可能)
deque.offerLast("E"); // 队尾添加(队列操作)
deque.push("F");      // 栈顶添加(栈操作)
System.out.println(deque.pollFirst()); // F(混合操作结果)

6. 常用实现类:

  • ArrayDeque:基于循环数组(默认容量16,性能最优)

  • LinkedList:基于双向链表(支持索引访问)

  • LinkedBlockingDeque:线程安全阻塞双端队列


四、与其他集合类对比
特性 Queue/Deque List Set Map
数据结构 线性序列 线性序列 哈希表/树 键值对
元素顺序 FIFO/LIFO/优先级 插入顺序/索引 无序/排序 无序/键排序
重复元素 允许 允许 不允许 值允许,键不允许
空值支持 部分实现支持 允许 HashSet允许 HashMap允许值
访问方式 端点访问 索引/迭代器 迭代器 键访问
典型实现 ArrayDeque, PriorityQueue ArrayList, LinkedList HashSet, TreeSet HashMap, TreeMap

五、使用场景与最佳实践

1. 队列场景:

  • 任务调度:ThreadPoolExecutor 使用 BlockingQueue

  • 消息传递:生产者-消费者模式

  • 广度优先搜索(BFS)

2. 双端队列场景:

  • 撤销操作栈:ArrayDeque 替代 Stack

  • 滑动窗口算法

  • 工作窃取算法(Work Stealing)

3. 选择指南:


六、常见问题

Q1:Queue和Deque的主要区别是什么?

A:

  1. 功能定位

    • Queue 是标准FIFO队列(尾部添加,头部移除)

    • Deque 是双端队列,扩展了Queue,支持两端操作

  2. 操作能力

    • Queue 只有队头出队(poll)、队尾入队(offer)

    • Deque 增加offerFirst/pollFirst等双端操作方法

  3. 栈功能

    • Deque 可直接作为栈使用(push/pop方法)

    • Queue 没有原生栈操作支持

Q2:ArrayDeque和LinkedList如何选择?

A:

  • ArrayDeque

    • 基于循环数组,内存连续

    • 两端操作时间复杂度O(1)

    • 随机访问更快,CPU缓存友好

    • 推荐场景:大多数队列/栈需求(默认选择)

  • LinkedList

    • 基于双向链表,内存分散

    • 支持List接口的索引访问

    • 插入删除中间元素更高效

    • 推荐场景

      • 需要同时使用队列和列表功能

      • 需要频繁在中间位置插入/删除

Q3:阻塞队列是什么?常用实现有哪些?

A:

  • 阻塞队列 :当队列满时阻塞生产者,队列空时阻塞消费者(BlockingQueue接口)

  • 常用实现

    • ArrayBlockingQueue:数组实现的有界队列

    • LinkedBlockingQueue:链表实现的可选有界队列

    • PriorityBlockingQueue:带优先级的无界阻塞队列

    • SynchronousQueue:不存储元素的直接传递队列

Q4:Deque如何替代Stack?

A:

  1. Java官方推荐用Deque替代Stack

  2. 转换方式:

    java 复制代码
    Deque<Integer> stack = new ArrayDeque<>();
    stack.push(1);          // 入栈 = addFirst()
    int top = stack.peek(); // 查看栈顶 = peekFirst()
    int pop = stack.pop();  // 出栈 = removeFirst()
  3. 优势

    • 避免Stack的同步开销(Vector实现)

    • 更统一的集合API

    • 更好的性能(特别是ArrayDeque

Q5:PriorityQueue的排序原理?

A:

  • 基于堆数据结构(默认最小堆)

  • 排序规则:

    • 自然排序:元素实现Comparable

    • 定制排序:构造时传入Comparator

  • 特点:

    • 队头总是当前最值元素

    • 入队/出队时间复杂度O(log n)

    • 不支持null元素

Q6:如何使用Deque同时作为队列和栈?

A:

Deque可以同时支持队列和栈操作,但必须避免混用API

  1. 队列模式(FIFO):固定使用尾部添加+头部移除组合
java 复制代码
// 推荐API组合
deque.offer(e);      // 入队(尾部)
String item = deque.poll(); // 出队(头部)
  1. 栈模式(LIFO):固定使用头部添加+头部移除组合
java 复制代码
// 推荐API组合
deque.push(e);       // 入栈(头部)
String top = deque.pop();  // 出栈(头部)
  1. 危险操作:混用API会导致数据顺序混乱
java 复制代码
// 错误示例(导致数据顺序不可预测)
deque.push("A");  // 栈操作(头部插入)
deque.offer("B"); // 队列操作(尾部插入)
// 此时队列:A<-B,但栈顶是A

Q7:为什么Java推荐用Deque代替Stack类?

A:

除了之前提到的性能优势,API设计也更合理:

  1. Stack的缺陷API

    java 复制代码
    // 老式Stack API(继承自Vector)
    stack.addElement(e);  // 非标准方法名
    stack.insertElementAt(e, 0); // 危险的低效操作
  2. Deque的标准栈API

    java 复制代码
    deque.push(e);  // 标准栈操作
    deque.pop();    // 直观的LIFO语义
    deque.peek();   // 查看栈顶
  3. 额外优势 :Deque的栈操作时间复杂度均为O(1),而Stack的insertElementAt(0)是O(n)


七、高频面试进阶问题
  1. poll()remove()有什么区别?

    • 行为相同:移除并返回队头元素

    • 空队列时:poll()返回nullremove()抛异常

  2. ArrayDeque初始容量是多少?如何扩容?

    • 默认初始容量16

    • 扩容规则:加倍容量(16→32→64...)

    • 重要特性:容量总是2的幂(位运算优化)

  3. 阻塞队列的put()offer()区别?

    java 复制代码
    // 阻塞方法(无限等待)
    void put(E e) throws InterruptedException;
    
    // 非阻塞方法
    boolean offer(E e, long timeout, TimeUnit unit); // 限时等待
    boolean offer(E e);                              // 立即返回
  4. 为什么LinkedList实现了ListDeque

    • 设计上支持多种访问方式:

      • 列表功能:索引访问/中间插入

      • 队列功能:FIFO操作

      • 双端功能:两端高效操作

    • 体现了接口隔离原则

相关推荐
别摸我的婴儿肥6 分钟前
从0开始LLM-注意力机制-4
人工智能·python·算法
oh,huoyuyan7 分钟前
从效率瓶颈到自动化:火语言 RPA 在日常工作中的技术实践
java·前端·rpa
静若繁花_jingjing7 分钟前
电商项目_秒杀_初步分析
java
LZQqqqqo12 分钟前
C# 类 封装 属性 练习题
java·前端·c#
Mr.小海24 分钟前
金融大模型与AI在金融业务中的应用调研报告(2025年)
人工智能·算法·机器学习·chatgpt·金融·gpt-3·文心一言
用户44726719331027 分钟前
Redis + Caffeine 实现高效的两级缓存架构
java
Java水解40 分钟前
美团Java面试题、笔试题(含答案)
java·后端·面试
苇柠42 分钟前
Java常用API(1)
java·开发语言
雪碧聊技术1 小时前
HashMap和Hashtable的区别
java·hashmap·hashtable
一只叫煤球的猫1 小时前
这些 Spring Boot 默认配置不改,迟早踩坑
java·spring boot·后端