【Java】ArrayDeque 详解:双端队列的高效实践指南
大家好,我是云扬~ 今天来和大家深入聊聊 Java 中的ArrayDeque—— 这个被很多开发者忽略,但性能极佳的双端队列实现。在日常开发中,我们经常会用到栈(Stack)或队列(Queue),而ArrayDeque作为两者的 “全能替代者”,其效率和灵活性值得我们重点掌握。
一、ArrayDeque 简介:为什么推荐用它?
在 Java 集合框架中,ArrayDeque是基于循环数组实现的双端队列(Deque 接口实现类),支持在队列两端快速插入、删除和访问元素。它的核心优势的在于:
- 🔹 比传统
Stack类高效:避免了synchronized关键字带来的线程安全开销,插入删除操作均为 O (1) 时间复杂度 - 🔹 比
LinkedList更高效:数组结构的随机访问性能优于链表,尤其在频繁操作场景下 - 🔹 功能全面:既可作为栈(LIFO)使用,也可作为队列(FIFO)使用,还支持双端操作
注意:
ArrayDeque不支持 null 元素,且非线程安全。多线程环境下需手动同步或者使用线程安全的队列(如使用)。Deque<E> deque = new ConcurrentLinkedDeque<>();
二、ArrayDeque 实战:栈与队列的完整用法
1. 作为栈(Stack)使用
栈遵循 “后进先出”(LIFO)原则,ArrayDeque提供了push()、pop()、peek()等专用方法,用法比Stack类更简洁:
import java.util.ArrayDeque;
import java.util.Iterator;
public class ArrayDequeAsStack {
public static void main(String[] args) {
// 1. 声明并初始化栈
ArrayDeque<String> stack = new ArrayDeque<>();
// 2. 入栈(添加元素到栈顶)
stack.push("Java基础");
stack.push("Spring框架");
stack.push("微服务架构");
System.out.println("入栈后:" + stack); // [微服务架构, Spring框架, Java基础]
// 3. 查看栈顶元素(不删除)
String top = stack.peek();
System.out.println("栈顶元素:" + top); // 微服务架构
// 4. 出栈(删除并返回栈顶元素)
String popElement = stack.pop();
System.out.println("出栈元素:" + popElement); // 微服务架构
System.out.println("出栈后:" + stack); // [Spring框架, Java基础]
// 5. 修改栈顶元素(先出栈再入栈)
stack.pop(); // 弹出Spring框架
stack.push("SpringBoot实战");
System.out.println("修改后:" + stack); // [SpringBoot实战, Java基础]
// 6. 遍历查找元素
Iterator> iterator = stack.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if (element.contains("Spring")) {
System.out.println("找到目标元素:" + element); // SpringBoot实战
}
}
}
}
2. 作为队列(Queue)使用
队列遵循 “先进先出”(FIFO)原则,ArrayDeque实现了Queue接口的offer()、poll()、peek()等方法,性能优于LinkedList:
import java.util.ArrayDeque;
public class ArrayDequeAsQueue {
public static void main(String[] args) {
// 1. 声明并初始化队列
ArrayDeque<String> queue = new ArrayDeque<>();
// 2. 入队(添加元素到队尾)
queue.offer("用户A");
queue.offer("用户B");
queue.offer("用户C");
System.out.println("入队后:" + queue); // [用户A, 用户B, 用户C]
// 3. 查看队首元素(不删除)
String head = queue.peek();
System.out.println("队首元素:" + head); // 用户A
// 4. 出队(删除并返回队首元素)
String pollElement = queue.poll();
System.out.println("出队元素:" + pollElement); // 用户A
System.out.println("出队后:" + queue); // [用户B, 用户C]
// 5. 修改队列元素(先出队再入队)
queue.poll(); // 弹出用户B
queue.offer("用户D");
System.out.println("修改后:" + queue); // [用户C, 用户D]
// 6. 查找元素(包含判断)
boolean contains = queue.contains("用户C");
System.out.println("队列是否包含用户C:" + contains); // true
}
}
3. 与 LinkedList 的队列用法对比
虽然LinkedList也实现了Deque接口,但在队列操作上,ArrayDeque的性能更优。以下是LinkedList的队列用法作为参考:
import java.util.LinkedList;
public class LinkedListAsQueue {
public static void main(String[] args) {
LinkedList<String> queue = new LinkedList<>();
// 入队
queue.offer("订单1");
queue.offer("订单2");
// 出队
queue.poll(); // 移除订单1
// 修改元素
queue.poll(); // 移除订单2
queue.offer("订单3");
// 查找元素
String first = queue.get(0); // 获取队首元素(订单3)
boolean hasOrder3 = queue.contains("订单3"); // true
System.out.println("队首元素:" + first + ",是否包含订单3:" + hasOrder3);
}
}
三、核心原理:Deque 接口与底层实现
1. Deque 接口的方法映射
Deque接口整合了Queue和Stack的核心能力,方法对应关系如下:
| 功能场景 | Queue 接口方法 | Deque 接口等效方法 | Stack 类方法 |
|---|---|---|---|
| 入队 / 入栈 | offer(e) | addLast(e) | push(e) |
| 出队 / 出栈 | poll() | pollFirst() | pop() |
| 查看首元素 | peek() | peekFirst() | peek() |
| 入队(异常) | add(e) | addLast(e) | – |
2. 底层实现细节
- 循环数组:
ArrayDeque的底层是一个动态扩容的循环数组,通过head和tail指针标记队列的首尾位置,避免数组移动带来的性能损耗 - 扩容机制:当元素数量达到数组容量时,会触发扩容,新容量为原容量的 2 倍(初始容量为 16),并通过
Arrays.copyOf()复制元素 - 禁止 null 元素:插入 null 会抛出
NullPointerException,这是为了避免与poll()方法返回 null 表示队列空的逻辑冲突
四、核心方法剖析
| 方法名 | 功能描述 | 时间复杂度 |
|---|---|---|
addFirst(E e) | 在队列头部插入元素,空间不足则扩容 | O(1) |
addLast(E e) | 在队列尾部插入元素,空间不足则扩容 | O(1) |
pollFirst() | 删除并返回头部元素,队列为空返回 null | O(1) |
pollLast() | 删除并返回尾部元素,队列为空返回 null | O(1) |
peekFirst() | 返回头部元素(不删除),队列为空返回 null | O(1) |
peekLast() | 返回尾部元素(不删除),队列为空返回 null | O(1) |
size() | 返回元素个数 | O(1) |
contains(Object o) | 判断是否包含指定元素 | O(n) |
五、使用场景与注意事项
1. 推荐场景
- 需要频繁进行栈或队列操作的场景(如算法实现、任务队列)
- 单线程环境下的高效数据操作(避免
Stack的同步开销) - 需要双端操作的场景(如滑动窗口算法)
2. 注意事项
- 非线程安全:多线程环境下需使用
Deque<E> deque = new ConcurrentLinkedDeque<>(); - 不支持 null 元素:插入前需确保元素非空
- 遍历顺序:迭代器按元素插入顺序遍历(队列模式下 FIFO,栈模式下 LIFO)
六、总结
ArrayDeque是 Java 集合框架中性能出色的双端队列实现,兼具栈和队列的功能,且在大多数场景下性能优于Stack和LinkedList。掌握它的核心用法和底层原理,能帮助我们在开发中选择更合适的数据结构,提升程序效率。
如果需要在多线程环境下使用,记得进行同步处理;如果需要存储 null 元素,则应选择LinkedList。希望这篇详解能帮助大家更好地理解和使用ArrayDeque~
欢迎在评论区分享你的使用经验或疑问,我们一起交流进步!



