Queue
字数: 0 字 时长: 0 分钟

1. Queue
Queue 表示:
队列普通队列通常遵循:
FIFO
First In First Out
先进先出也就是:
队尾添加元素
↓
队首取出元素例如:
入队
A → B → C
出队
A → B → C
↑
先取 A常用方法:
| 操作 | 抛异常版本 | 返回特殊值版本 |
|---|---|---|
| 添加元素 | add() | offer() |
| 删除队首 | remove() | poll() |
| 查看队首 | element() | peek() |
其中日常使用通常更常见:
offer()
poll()
peek()因为队列为空或添加失败时,可以通过:
false
null表示,而不是直接抛出异常。
这里主要看三个常见实现:
Queue
├── ArrayDeque
├── LinkedList
└── PriorityQueue1.1 ArrayDeque
ArrayDeque 是一个基于可扩容数组实现的双端队列。
Array
+
Deque
↓
ArrayDequeDeque 全称:
Double Ended Queue
双端队列意味着:
队首和队尾都可以添加、删除元素。
1.1.1 底层结构
ArrayDeque 底层使用循环数组。
可以简单理解:
┌────────────────────┐
↓ │
[ ][A][B][C][ ][ ][ ][ ]────┘
↑ ↑
head tail其中:
head
↓
指向队首元素
tail
↓
指向下一次可以插入元素的位置由于底层采用:
循环数组所以:
head 不一定等于 0
tail 也不一定始终位于 head 后面原来的结构图:

1.1.2 基本使用
Deque<String> deque =
new ArrayDeque<>();
// 队尾添加
deque.offerLast(
"周芷若"
);
deque.offerLast(
"张无忌"
);
deque.offerLast(
"赵敏"
);
// 查看队首
String first =
deque.peekFirst();
// 队首删除
deque.pollFirst();
// 判断元素是否存在
boolean exists =
deque.contains(
"张无忌"
);
System.out.println(
exists
);如果只是把它当普通队列:
Queue<String> queue =
new ArrayDeque<>();
queue.offer(
"周芷若"
);
queue.offer(
"张无忌"
);
queue.offer(
"赵敏"
);
System.out.println(
queue.poll()
);输出:
周芷若符合:
FIFO
先进先出1.1.3 双端操作
因为它实现了:
Deque所以可以操作两端:
deque.addFirst(
"A"
);
deque.addLast(
"B"
);
deque.pollFirst();
deque.pollLast();
deque.peekFirst();
deque.peekLast();可以理解:
队首 队尾
↓ ↓
A ⇄ B ⇄ C ⇄ D
↑ ↑
First 操作 Last 操作1.1.4 ArrayDeque 的特点
主要特点:
底层基于循环数组
支持队首、队尾操作
容量不足时自动扩容
不支持通过下标随机访问
不允许存放 null需要特别注意:
ArrayDeque虽然底层是数组,但它没有提供get(index)这样的随机访问 API。
所以不能写:
// 不存在
// deque.get(0);ArrayDeque 很适合实现:
普通队列
双端队列
栈例如实现栈:
Deque<String> stack =
new ArrayDeque<>();
stack.push("A");
stack.push("B");
stack.push("C");
System.out.println(
stack.pop()
);输出:
C所以现代 Java 中:
实现队列
↓
ArrayDeque
实现栈
↓
ArrayDeque都非常常见。
1.2 LinkedList
LinkedList 前面在 List 中已经讲过。
它比较特殊,因为同时实现了:
List
Deque接口。
源码关系:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>,
Deque<E>,
Cloneable,
Serializable {
}所以:
LinkedList
↓
既可以当 List
↓
也可以当 Queue / Deque需要注意:
LinkedList可以提供普通队列和栈式操作,但它并不等于PriorityQueue,也不具备优先级队列的排序语义。
1.2.1 LinkedList 作为队列
Queue<String> queue =
new LinkedList<>();
// 入队
queue.offer(
"周芷若"
);
queue.offer(
"赵敏"
);
queue.offer(
"张无忌"
);
System.out.println(
queue
);结果:
[周芷若, 赵敏, 张无忌]删除队首:
String first =
queue.poll();
System.out.println(
first
);输出:
周芷若查看队首但不删除:
String first =
queue.peek();整个过程:
offer()
↓
队尾添加
poll()
↓
队首取出并删除
peek()
↓
只查看队首1.2.2 LinkedList 作为双端队列
如果声明成:
Deque<String> deque =
new LinkedList<>();同样可以:
deque.addFirst(
"A"
);
deque.addLast(
"B"
);
deque.pollFirst();
deque.pollLast();因此:
LinkedList
↓
双向链表
↓
天然适合两端插入和删除1.2.3 LinkedList 和 ArrayDeque
两者都实现:
Deque但是底层结构不同:
LinkedList
↓
双向链表
ArrayDeque
↓
循环数组简单比较:
| 对比 | ArrayDeque | LinkedList |
|---|---|---|
| 底层结构 | 循环数组 | 双向链表 |
| 两端插入删除 | 快 | 快 |
是否支持 null | 不支持 | 支持 |
| 额外节点引用 | 无 | 有 |
| 内存占用 | 通常更低 | 通常更高 |
| 作为普通队列 | 推荐 | 可以 |
普通的:
队列
双端队列
栈场景中,一般优先考虑:
ArrayDeque而不是因为:
LinkedList 是链表就默认认为它一定更快。
1.3 PriorityQueue
PriorityQueue 是:
优先级队列。
普通队列:
先进先出
FIFO而优先级队列:
谁优先级更高
↓
谁先出队它底层通常可以理解为:
数组
+
二叉堆Java 的 PriorityQueue 使用:
堆结构维护队首元素。
默认情况下:
按照元素的自然顺序,较小的元素具有更高优先级。
例如:
Queue<Integer> queue =
new PriorityQueue<>();
queue.offer(30);
queue.offer(10);
queue.offer(20);
System.out.println(
queue.poll()
);
System.out.println(
queue.poll()
);
System.out.println(
queue.poll()
);输出:
10
20
30虽然插入顺序:
30
10
20但出队顺序:
10
20
301.3.1 基本使用
PriorityQueue<String> queue =
new PriorityQueue<>();
queue.offer(
"张无忌"
);
queue.offer(
"赵敏"
);
queue.offer(
"周芷若"
);
// 查看优先级最高的队首元素
String first =
queue.peek();
// 取出并删除队首元素
String value =
queue.poll();
// 判断是否存在
boolean exists =
queue.contains(
"赵敏"
);需要注意:
不要根据
PriorityQueue直接打印或 foreach 遍历的结果判断整体排序。
例如:
System.out.println(
queue
);得到的内部顺序:
不保证是完整排序后的顺序PriorityQueue 只保证:
peek()
poll()每次访问到当前:
优先级最高的队首元素如果需要按优先级顺序全部取出:
while (
!queue.isEmpty()
) {
System.out.println(
queue.poll()
);
}1.3.2 PriorityQueue 的排序规则
元素的优先级可以来自:
Comparable或者:
Comparator如果元素本身实现:
Comparable可以按照自然顺序排列。
如果希望自己定义优先级,则传入:
Comparator例如学生:
class Student {
private final String name;
private final int chineseScore;
private final int mathScore;
public Student(
String name,
int chineseScore,
int mathScore) {
this.name =
name;
this.chineseScore =
chineseScore;
this.mathScore =
mathScore;
}
public String getName() {
return name;
}
public int getTotalScore() {
return chineseScore
+ mathScore;
}
@Override
public String toString() {
return name
+ ":"
+ getTotalScore();
}
}按照:
总成绩从高到低设置优先级:
Comparator<Student> comparator =
Comparator.comparingInt(
Student::getTotalScore
).reversed();创建:
PriorityQueue<Student> queue =
new PriorityQueue<>(
comparator
);添加:
queue.offer(
new Student(
"周芷若",
80,
90
)
);
queue.offer(
new Student(
"张无忌",
95,
95
)
);
queue.offer(
new Student(
"赵敏",
90,
95
)
);
queue.offer(
new Student(
"小昭",
90,
80
)
);按照优先级依次取出:
while (
!queue.isEmpty()
) {
System.out.println(
queue.poll()
);
}结果:
张无忌:190
赵敏:185
周芷若:170
小昭:170总成绩越高:
优先级越高
↓
越早被 poll()需要注意:
当两个元素比较结果为
0时,PriorityQueue不保证它们按照插入顺序出队。
所以:
周芷若 170
小昭 170两者谁先出来,不应该依赖上面的示例顺序。
1.3.3 PriorityQueue 的特点
可以简单记:
PriorityQueue
↓
优先级队列
↓
底层使用堆
↓
队首始终保持高优先级元素默认:
自然顺序自定义:
Comparator常见方法:
offer()
↓
加入元素
peek()
↓
查看当前优先级最高元素
poll()
↓
取出当前优先级最高元素PriorityQueue 同样不允许:
null元素。
2. 小结
三个常见队列实现:
| 实现 | 底层结构 | 顺序规则 | 主要特点 |
|---|---|---|---|
ArrayDeque | 循环数组 | 队列顺序 | 高效双端队列 |
LinkedList | 双向链表 | 队列顺序 | 同时实现 List、Deque |
PriorityQueue | 堆 | 优先级顺序 | 优先级最高的元素先出队 |
可以简单记:
普通队列
↓
ArrayDeque
双端队列
↓
ArrayDeque
LinkedList
↓
既能作为 List
也能作为 Deque
优先级队列
↓
PriorityQueue三者最核心的区别:
ArrayDeque
↓
循环数组
↓
适合普通队列 / 双端队列 / 栈
LinkedList
↓
双向链表
↓
同时具备 List 和 Deque 能力
PriorityQueue
↓
堆
↓
不按照普通 FIFO
↓
按照优先级出队一句话概括:
普通 Queue 强调先进先出,Deque 强调两端都可以操作,而 PriorityQueue 强调“谁优先级高,谁先出队”。