Deque (Double-Ended Queue) 接口继承自 Queue 接口。它提供了一个双向功能,允许在队列的两端进行添加和移除操作。
-
核心原则: 允许在队头和队尾操作元素。
-
用途: 可以用作 FIFO 队列、LIFO 栈,或者双端队列。
| 操作目的 | 作为 LIFO 栈 (Stack) | 作为 FIFO 队列 (Queue) | 抛出异常 (严格) | 返回特殊值 (健壮) |
|---|---|---|---|---|
| 添加元素 | 在头部添加 | 在尾部添加 | addFirst(), push(), add() |
offerFirst(), offerLast(), offer() |
| 栈方法 | push(E e) |
N/A | push() |
N/A |
| 入队方法 | N/A | add(E e) |
add() |
offer(E e) |
| 移除元素 | 从头部移除 | 从头部移除 | removeFirst(), pop(), remove() |
pollFirst(), poll() |
| 出栈方法 | pop() |
N/A | pop() |
N/A |
| 出队方法 | N/A | remove(), poll() |
remove() |
poll() |
| 查看元素 | 查看头部元素 | 查看头部元素 | getFirst(), element() |
peekFirst(), peek() |
| 栈方法 | N/A | N/A | getFirst() |
peekFirst() |
| 队头方法 | N/A | element(), peek() |
element() |
peek() |
| List.of:创建一个不可变的(immutable)List。 | ||||
| 不可变性 (Immutability): |
-
你不能向这个列表添加(
add)或删除(remove)元素。 -
你也不能替换(
set)列表中的任何元素。 -
任何尝试修改列表的操作都会抛出
UnsupportedOperationException(不支持的操作异常)。
不允许null元素: -
你不能使用
List.of()来创建一个包含null元素的列表。 -
如果尝试传入
null,它会立即抛出NullPointerException(空指针异常)。
便捷性 (Convenience): -
它提供了一种非常简洁的方式来初始化一个列表,而不需要先创建
new ArrayList<>()然后再逐个add元素。
List<String> newList = List.of("Apple", "Banana", "Cherry");
Queue 接口定义了标准的、线性的数据结构——队列 (Queue)。
-
核心原则: 先进先出 (FIFO - First-In, First-Out)。
-
操作限制: 元素只能在队尾 (Tail) 添加,并在队头 (Head) 移除。
| 操作目的 | 方法(抛出异常) | 方法(返回特殊值) | 描述 |
|---|---|---|---|
| 添加/入队 | add(E e) |
offer(E e) |
将元素添加到队尾。 |
| 移除/出队 | remove() |
poll() |
移除并返回队头元素。 |
| 查看队头 | element() |
peek() |
返回队头元素,但不移除。 |
现代 Java 的首选:ArrayDeque
在绝大多数情况下,如果你只是需要一个 Deque (或者一个 Queue 队列,或一个 Stack 栈),ArrayDeque 是你的首选。
ArrayDeque (在 Java 6 引入) 是一个“数组双端队列”。
-
内部结构:它使用一个可调整大小的循环数组。
-
优点:
-
非常快:在两端(头部和尾部)添加或删除元素非常高效(摊销 O(1)),因为它只是在数组中移动指针。
-
内存高效:它没有
LinkedList中每个元素所需的额外“节点(Node)”对象的开销。 -
CPU 友好:数组数据在内存中是连续的,这有助于 CPU 缓存(数据局部性)。
-
-
缺点:
-
它不允许存储
null元素。 -
它不实现
List接口(它只专注于Deque的功能)。
-
经典的(但通常较慢的)选择:LinkedList
LinkedList (你之前问过的) 是一个“链表”。
-
内部结构:它使用一个双向链表(每个元素都是一个“节点”,指向上一个和下一个节点)。
-
优点:
-
它也实现了
List接口,所以如果你需要一个同时是List又是Deque的东西,它是你唯一的选择。 -
它允许存储
null元素。
-
-
缺点:
-
性能通常较差:虽然在两端添加/删除也是 O(1),但它涉及创建/销毁节点对象,这会给垃圾收集器(GC)带来压力。
-
内存开销更大:每个元素都需要一个额外的
Node对象来存储next和previous的引用。
-
💡 如何选择:一个简单的规则
-
我需要一个
Deque(双端队列) 或Queue(队列) 吗?-
👉 使用
ArrayDeque。 -
Deque<String> myQueue = new ArrayDeque<>();
-
-
我需要一个
Stack(栈) 吗?-
(Java 有一个旧的
Stack类,但官方不推荐使用它)。 -
👉 使用
ArrayDeque。Deque接口提供了push()和pop()方法,完美地实现了栈。 -
Deque<String> myStack = new ArrayDeque<>();
-
-
我需要一个
List(列表) 吗?-
👉 使用
ArrayList(如果你需要快速的get(index)访问)。 -
👉 使用
LinkedList(如果你需要非常频繁地在列表_中间_ 插入/删除)。
-
总结:
LinkedList 试图同时做好 List 和 Deque 两件事,但 ArrayDeque 是一个专门为 Deque 和 Stack 操作优化的类。