欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 手游 > 深入解析Java中的双向队列(Deque):操作、实现与应用

深入解析Java中的双向队列(Deque):操作、实现与应用

2025/4/16 3:59:42 来源:https://blog.csdn.net/qq_39842184/article/details/147234919  浏览:    关键词:深入解析Java中的双向队列(Deque):操作、实现与应用

双向队列(Deque,全称Double-Ended Queue)是一种允许在队列的头部和尾部高效插入、删除和访问元素的数据结构。它既支持先进先出(FIFO)的队列模式,也支持后进先出(LIFO)的栈模式,是Java集合框架中功能最灵活的数据结构之一。本文将深入探讨Java中双向队列的核心操作、实现类及其典型应用场景。

阅读本文前如果你对队列不理解,请阅读本人的另外一篇文章!

深入理解Java中的队列:核心操作、实现与应用-CSDN博客


一、双向队列的核心操作

Java通过java.util.Deque接口定义了双向队列的操作方法,其核心方法分为头部操作尾部操作两类,且每种操作均提供“抛出异常”和“返回特殊值”两种形式:

操作类型头部操作(抛出异常)头部操作(返回特殊值)尾部操作(抛出异常)尾部操作(返回特殊值)描述
插入元素addFirst(e)offerFirst(e)addLast(e)offerLast(e)在头/尾部插入元素,失败时抛出异常或返回false
移除元素removeFirst()pollFirst()removeLast()pollLast()移除头/尾部元素,队列空时抛出异常或返回null
查看元素getFirst()peekFirst()getLast()peekLast()获取但不移除头/尾部元素,空队列时抛出异常或返回null

示例代码:

Deque<String> deque = new ArrayDeque<>();
deque.offerFirst("A");  // 头部插入"A"
deque.offerLast("B");   // 尾部插入"B"
String first = deque.pollFirst();  // 移除并返回"A"
String last = deque.peekLast();    // 查看"B"(不移除)

二、Java双向队列的常见实现类
  1. ArrayDeque

    • 底层结构:基于动态扩展的循环数组实现,内存紧凑,访问高效。

    • 特点

      • 默认初始容量为16,可自动扩容。

      • 插入/删除操作的时间复杂度为O(1)

      • 非线程安全,性能优于LinkedList

    • 适用场景:高频次的两端操作(如栈模拟、缓存)。

  2. LinkedList

    • 底层结构:基于双向链表实现,天然支持双向操作。

    • 特点

      • 插入/删除元素时无需移动其他元素,适合频繁增删的场景。

      • 支持索引访问(但效率低,时间复杂度为O(n))。

      • 非线程安全。

    • 适用场景:需要同时作为列表和双向队列使用的场景。

  3. ConcurrentLinkedDeque

    • 底层结构:基于无锁算法的并发双向链表。

    • 特点

      • 线程安全,适用于高并发环境。

      • 无界队列,支持非阻塞操作。

    • 适用场景:多线程任务调度、消息传递。

  4. LinkedBlockingDeque

    • 底层结构:基于链表的阻塞双向队列。

    • 特点

      • 可设置容量上限(有界队列)。

      • 提供阻塞式的插入/删除方法(如putFirst()takeLast())。

    • 适用场景:生产者-消费者模型,需双向阻塞操作的场景。


三、双向队列的典型应用场景
  1. 实现栈(Stack)
    Java官方推荐使用Deque替代传统的Stack类,因为Deque提供了更高效的栈操作:

    Deque<Integer> stack = new ArrayDeque<>();
    stack.push(1);      // 入栈
    stack.push(2);
    int top = stack.pop();  // 出栈(返回2)

  2. 滑动窗口算法
    在解决数组/字符串的子区间问题时,双向队列可高效维护窗口内的极值:

    // 求滑动窗口最大值(LeetCode 239)
    Deque<Integer> deque = new ArrayDeque<>();
    for (int i = 0; i < nums.length; i++) {while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {deque.pollLast();  // 移除尾部较小的元素索引}deque.offerLast(i);// 移除超出窗口头部的索引if (deque.peekFirst() == i - k) {deque.pollFirst();}// 记录当前窗口最大值if (i >= k - 1) {result[i - k + 1] = nums[deque.peekFirst()];}
    }

  3. LRU缓存淘汰算法
    双向队列结合哈希表可高效实现LRU(最近最少使用)缓存策略:

    class LRUCache {private Deque<Integer> deque;  // 维护访问顺序private Map<Integer, Integer> map;private int capacity;public void refer(int key) {if (!map.containsKey(key)) {if (deque.size() == capacity) {int last = deque.removeLast();  // 淘汰最久未使用的元素map.remove(last);}} else {deque.remove(key);  // 移除旧位置}deque.offerFirst(key);  // 更新为最近访问}
    }

  4. 回文检测
    双向队列可快速判断字符串是否为回文:

    Deque<Character> deque = new ArrayDeque<>();
    for (char c : str.toCharArray()) {deque.addLast(c);
    }
    boolean isPalindrome = true;
    while (deque.size() > 1) {if (!deque.pollFirst().equals(deque.pollLast())) {isPalindrome = false;break;}
    }

  5. 多线程任务窃取(Work Stealing)
    在Fork/Join框架中,ConcurrentLinkedDeque允许工作线程从其他线程的队列尾部窃取任务,实现负载均衡。


四、如何选择双向队列的实现类?
场景需求推荐实现类理由
高频次两端操作(单线程)ArrayDeque内存连续,性能最优
需要链表特性(如索引访问)LinkedList支持列表操作,但两端操作性能略低于ArrayDeque
高并发环境ConcurrentLinkedDeque无锁并发,非阻塞操作
阻塞式双向队列LinkedBlockingDeque支持容量限制和阻塞等待

五、双向队列 vs 普通队列 vs 栈
特性双向队列(Deque)普通队列(Queue)栈(Stack)
插入/删除端头部和尾部尾部插入,头部删除仅顶部操作(LIFO)
灵活性最高(支持队列+栈操作)仅支持FIFO仅支持LIFO
典型实现ArrayDeque, LinkedListLinkedList, ArrayDequeDeque(推荐替代Stack)

六、总结

双向队列(Deque)凭借其两端操作的高效性,成为Java中解决复杂问题的利器。无论是实现栈、优化算法性能(如滑动窗口),还是设计高并发系统,双向队列都能提供简洁而强大的支持。开发者应根据具体场景选择ArrayDeque(性能优先)、LinkedList(功能多样)或并发实现类,充分发挥其灵活性和高效性。

扩展思考

  • 如何用Deque实现一个线程安全的阻塞栈?

  • 探索Deque在图形搜索算法(如BFS的变种)中的应用。

  • 对比DequeQueue在资源池管理中的优劣。

如果对你有帮助,请帮忙点个赞! 

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词