Java中双端队列的多种实现类详解

双端队列(Deque,即Double Ended Queue)是Java集合框架中的重要组成部分,它允许在队列的两端高效地插入和删除元素。本文我将深入剖析Java中四种主要的双端队列实现类:ArrayDequeLinkedListConcurrentLinkedDequeLinkedBlockingDeque,通过源码分析和性能对比,帮你掌握在不同应用场景下双端队列的最优选择。

一、双端队列概述与核心接口

双端队列(Deque)是一种特殊的队列,支持在队列的头部和尾部进行高效的插入和删除操作。在Java中,Deque接口继承自Queue接口,定义了一套标准的双端操作方法:

public interface Deque<E> extends Queue<E> {
   
   
    // 头部操作
    void addFirst(E e);
    boolean offerFirst(E e);
    E removeFirst();
    E pollFirst();
    E getFirst();
    E peekFirst();
    
    // 尾部操作
    void addLast(E e);
    boolean offerLast(E e);
    E removeLast();
    E pollLast();
    E getLast();
    E peekLast();
    
    // 栈操作
    void push(E e);
    E pop();
    
    // 迭代器
    Iterator<E> descendingIterator();
}

Deque接口提供了两套操作方法:一套在操作失败时抛出异常(如addFirst()),另一套返回特殊值(如offerFirst()返回false)。这种设计模式为不同使用场景提供了灵活性。

二、ArrayDeque:基于动态数组的双端队列实现

核心特性

  • 底层结构:循环数组,数组长度始终为2的幂
  • 扩容机制:容量不足时自动扩容为原容量的2倍
  • 性能特点:随机访问时间复杂度O(1),插入删除平均O(1)
  • 限制条件:不允许存储null元素

源码分析:循环数组的奥秘

ArrayDeque使用两个索引变量headtail来标记队列的头部和尾部:

transient Object[] elements; // 存储元素的数组
transient int head;          // 头部元素索引
transient int tail;          // 尾部元素的下一个位置索引

插入元素时的索引计算采用位运算,确保在数组范围内循环:

// 添加到尾部
public void addLast(E e) {
   
   
    if (e == null)
        throw new NullPointerException();
    elements[tail] = e;
    // 位运算等价于 (tail + 1) % elements.length
    if ((tail = (tail + 1) & (elements.length - 1)) == head)
        doubleCapacity(); // 扩容
}

常用方法示例

Deque<String> deque = new ArrayDeque<>();

// 添加元素
deque.addFirst("A");  // 头部插入
deque.offerLast("B"); // 尾部插入

// 访问元素
String first = deque.peekFirst()
/* * 基于双向链表实现双端队列结构 */ package dsa; public class Deque_DLNode implements Deque { protected DLNode header;//指向头节点(哨兵) protected DLNode trailer;//指向尾节点(哨兵) protected int size;//队列中元素的数目 //构造函数 public Deque_DLNode() { header = new DLNode(); trailer = new DLNode(); header.setNext(trailer); trailer.setPrev(header); size = 0; } //返回队列中元素数目 public int getSize() { return size; } //判断队列是否为空 public boolean isEmpty() { return (0 == size) ? true : false; } //取首元素(但不删除) public Object first() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); return header.getNext().getElem(); } //取末元素(但不删除) public Object last() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); return trailer.getPrev().getElem(); } //在队列前端插入新节点 public void insertFirst(Object obj) { DLNode second = header.getNext(); DLNode first = new DLNode(obj, header, second); second.setPrev(first); header.setNext(first); size++; } //在队列后端插入新节点 public void insertLast(Object obj) { DLNode second = trailer.getPrev(); DLNode first = new DLNode(obj, second, trailer); second.setNext(first); trailer.setPrev(first); size++; } //删除首节点 public Object removeFirst() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); DLNode first = header.getNext(); DLNode second = first.getNext(); Object obj = first.getElem(); header.setNext(second); second.setPrev(header); size--; return(obj); } //删除末节点 public Object removeLast() throws ExceptionQueueEmpty { if (isEmpty()) throw new ExceptionQueueEmpty("意外:双端队列为空"); DLNode first = trailer.getPrev(); DLNode second = first.getPrev(); Object obj = first.getElem(); trailer.setPrev(second); second.setNext(trailer); size--; return(obj); } //遍历 public void Traversal() { DLNode p = header.getNext(); while (p != trailer) { System.out.print(p.getElem()+" "); p = p.getNex
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值