ArrayDeque是实现高效FIFO和LIFO的首选,应按需选择端点操作:FIFO用offerLast()+pollFirst(),LIFO用offerFirst()+pollFirst();避免混合语义、随机访问及线程不安全使用。

Java双端队列(Deque)天然支持高性能的FIFO(先进先出)和LIFO(后进先出)行为,关键不在于“改造”,而在于**按需选择操作端点与方法组合**。ArrayDeque 是首选实现,它基于循环数组,无链表指针开销,插入/删除均摊时间复杂度为 O(1),且内存局部性好;LinkedList 虽也实现 Deque,但因链式结构、GC压力和缓存不友好,性能通常弱于 ArrayDeque。
用 ArrayDeque 实现高效 FIFO
FIFO 即队列语义:新元素加在尾部,旧元素从头部取出。应始终使用尾进 + 头出组合,避免混合操作影响语义清晰性与性能稳定性。
- 推荐方法对:`offerLast()` 入队 + `pollFirst()` 出队(失败返回 null,安全);或 `addLast()` + `removeFirst()`(失败抛异常,适合已知非空场景)
- 避免反模式:不用 `offerFirst()` + `pollFirst()` 模拟 FIFO——这实际是 LIFO;也不要用 `offerLast()` + `pollLast()`,虽逻辑上“最后进最后出”,但违背 FIFO 本意且易混淆
-
示例片段:
Deque<String> fifo = new ArrayDeque<>();
fifo.offerLast("task1");
fifo.offerLast("task2");
String first = fifo.pollFirst(); // 返回 "task1"
用 ArrayDeque 实现高效 LIFO
LIFO 即栈语义:新元素压入顶部,最后压入的最先弹出。Java 官方明确推荐用 Deque 替代过时的 Stack 类,核心就是统一使用头部操作(逻辑上的“栈顶”)。
- 标准栈方法映射:`push(e)` 等价于 `offerFirst(e)`,`pop()` 等价于 `pollFirst()`,`peek()` 等价于 `peekFirst()`
- 为什么选头部而非尾部:语义一致(所有栈操作集中在一端)、API 设计意图明确、与 `Stack` 类迁移兼容;若用 `offerLast()` + `pollLast()` 也可实现 LIFO,但偏离通用约定,可读性下降
- 注意 null 安全:`pollFirst()` 和 `peekFirst()` 在空时返回 null,若元素本身允许为 null,需额外判空逻辑;`pop()` 在空时抛 `NoSuchElementException`,适合强契约场景
性能关键细节与避坑指南
ArrayDeque 的高性能依赖于正确使用方式,几个易被忽略但影响显著的点:
立即学习“Java免费学习笔记(深入)”;
- 初始化容量:默认初始容量为 16。若预估大小(如处理固定批次任务),用 `new ArrayDeque(initialCapacity)` 避免多次扩容(扩容需数组复制)
- 禁止随机访问:Deque 不提供 `get(int index)`,强行用 `toArray()[i]` 或遍历索引会退化为 O(n);需索引访问请换用 ArrayList
- 线程不安全:ArrayDeque 和 LinkedList 均非线程安全。高并发场景下,不要加锁包裹整个 deque 操作(粗粒度锁瓶颈大),应考虑 `ConcurrentLinkedDeque`(无界、弱一致性)或业务层协调
- 避免混合语义:同一 deque 实例交替执行 FIFO 和 LIFO 操作(如一边 `offerLast()` 一边 `pollFirst()`,又突然 `push()`),会破坏数据顺序假设,调试困难且失去性能优势
何时选 LinkedList 而非 ArrayDeque
绝大多数场景下 ArrayDeque 更优,仅在以下特殊需求时考虑 LinkedList:
- 需要频繁在中间位置插入/删除(但此时已超出 deque 本职,应考虑 List)
- 必须存储大量 null 元素(ArrayDeque 允许 null,但某些老版本 JDK 文档曾误导;实测 Java 8+ 完全支持)
- 已有代码重度依赖 `LinkedList` 的 list 接口能力(如 `listIterator()`),且不愿拆分数据结构
单纯为了“双端队列”功能而选 LinkedList,是典型的过早优化误判——它的节点对象分配、引用跳转、GC负担,都比 ArrayDeque 的连续数组更重。



















