Java 实例 – 队列(Queue)用法:从入门到实战
你有没有在餐厅等位时排队的经历?前一个人点完餐,后一个人才能轮到。这种“先来先服务”的机制,就是队列(Queue)在生活中的真实写照。在 Java 编程中,队列是一种非常基础且重要的数据结构,它遵循“先进先出”(FIFO, First In First Out)的原则,广泛应用于任务调度、消息传递、缓冲处理等场景。
今天我们就来深入聊聊 Java 实例 – 队列(Queue)用法,通过实际代码和案例,带你一步步掌握它的核心用法。无论是初学者还是有一定经验的开发者,都能从中收获实用技巧。
什么是队列?它为什么重要?
队列是一种只能在一端插入(入队,enqueue),另一端删除(出队,dequeue)的线性数据结构。你可以把它想象成一条单行道的收费通道:车从入口进入,必须按顺序从出口离开,不能插队。
在 Java 中,java.util.Queue 是一个接口,定义了队列的基本操作。它的实现类有很多,比如 LinkedList、PriorityQueue、ArrayDeque 等。每种实现适用于不同场景,我们稍后会一一讲解。
⚠️ 注意:Java 中的 Queue 接口是
java.util包下的,使用前记得导入:import java.util.Queue;
常见队列实现类对比
在使用队列之前,先了解不同实现类的特点,有助于你做出合适的选择。
| 实现类 | 特点说明 | 适用场景 |
|---|---|---|
| LinkedList | 基于链表,支持动态扩容,线程不安全 | 通用场景,需要频繁插入/删除 |
| ArrayDeque | 基于循环数组,性能高,线程不安全 | 高性能队列,尤其适合栈/队列混合使用 |
| PriorityQueue | 优先级队列,元素按自然顺序或自定义排序 | 任务调度、堆排序、Top K 问题 |
这些类都实现了 Queue 接口,因此它们的使用方式非常相似。接下来我们通过实际代码来体验它们的差异。
基本操作:入队与出队
我们先从最基础的操作开始:入队(add / offer)和出队(remove / poll)。
import java.util.Queue;
import java.util.LinkedList;
public class QueueDemo {
public static void main(String[] args) {
// 创建一个队列实例,使用 LinkedList 实现
Queue<String> queue = new LinkedList<>();
// 入队操作:添加元素到队尾
queue.add("任务1"); // 添加任务1
queue.add("任务2"); // 添加任务2
queue.add("任务3"); // 添加任务3
System.out.println("队列当前内容:" + queue);
// 出队操作:从队头移除并返回元素
String firstTask = queue.remove(); // 移除并返回第一个任务
System.out.println("已处理任务:" + firstTask);
// 再次出队
String secondTask = queue.poll(); // 移除第一个任务,若为空返回 null
System.out.println("已处理任务:" + secondTask);
System.out.println("处理后队列内容:" + queue);
}
}
代码注释说明:
add()方法:向队列尾部添加元素。如果队列已满(如容量受限的队列),会抛出异常IllegalStateException。offer()方法:与add()类似,但若添加失败(如队列满),不会抛异常,而是返回false,更安全。remove()方法:移除队头元素。如果队列为空,会抛出NoSuchElementException。poll()方法:移除队头元素,若为空则返回null,适合在不确定队列是否有元素时使用。
💡 小贴士:在生产环境中,推荐使用
offer()和poll(),避免因空队列导致程序崩溃。
队列的查看操作:peek 与 element
有时候我们只想查看队头元素,但不希望它被移除。这时就可以用 peek() 和 element() 方法。
import java.util.Queue;
import java.util.LinkedList;
public class PeekDemo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.offer("任务A");
queue.offer("任务B");
queue.offer("任务C");
// peek():查看队头元素,但不移除
String first = queue.peek();
System.out.println("队头元素(不移除):" + first);
// element():查看队头元素,若为空则抛异常
String second = queue.element();
System.out.println("队头元素(强制获取):" + second);
System.out.println("队列当前状态:" + queue);
}
}
输出结果:
队头元素(不移除):任务A
队头元素(强制获取):任务A
队列当前状态:[任务A, 任务B, 任务C]
关键区别:
peek():安全,空队列返回null。element():不安全,空队列抛异常。
✅ 推荐:在不确定队列是否为空时,优先使用
peek()。
实际应用案例:模拟任务调度系统
现在我们来做一个真实的项目级例子:模拟一个任务调度系统。假设系统有多个任务需要按顺序处理,我们用队列来管理。
import java.util.Queue;
import java.util.LinkedList;
public class TaskScheduler {
private Queue<String> taskQueue;
public TaskScheduler() {
this.taskQueue = new LinkedList<>();
}
// 添加任务
public void addTask(String task) {
taskQueue.offer(task);
System.out.println("新增任务:" + task);
}
// 处理下一个任务
public void processNextTask() {
String task = taskQueue.poll();
if (task != null) {
System.out.println("正在处理:" + task);
} else {
System.out.println("没有待处理任务。");
}
}
// 查看当前任务数量
public int getTaskCount() {
return taskQueue.size();
}
// 打印所有任务
public void printAllTasks() {
System.out.println("当前待处理任务:" + taskQueue);
}
public static void main(String[] args) {
TaskScheduler scheduler = new TaskScheduler();
// 添加任务
scheduler.addTask("用户登录验证");
scheduler.addTask("数据备份");
scheduler.addTask("日志清理");
scheduler.printAllTasks();
System.out.println("任务总数:" + scheduler.getTaskCount());
// 处理任务
scheduler.processNextTask();
scheduler.processNextTask();
scheduler.processNextTask();
scheduler.processNextTask(); // 尝试处理空队列
}
}
输出结果:
新增任务:用户登录验证
新增任务:数据备份
新增任务:日志清理
当前待处理任务:[用户登录验证, 数据备份, 日志清理]
任务总数:3
正在处理:用户登录验证
正在处理:数据备份
正在处理:日志清理
没有待处理任务。
这个例子展示了队列在真实系统中的价值:它让任务有序执行,避免混乱,也便于扩展(比如加入优先级队列)。
高级用法:PriorityQueue 优先级队列
有些场景下,不是所有任务都一样重要。比如系统中“紧急故障修复”应该比“常规日志更新”优先处理。这时我们就需要 PriorityQueue。
import java.util.PriorityQueue;
public class PriorityTaskDemo {
public static void main(String[] args) {
// 创建优先级队列,按任务重要性排序(数值越小越优先)
PriorityQueue<Task> priorityQueue = new PriorityQueue<>();
// 添加任务
priorityQueue.offer(new Task("紧急修复", 1));
priorityQueue.offer(new Task("日志轮转", 3));
priorityQueue.offer(new Task("数据同步", 2));
priorityQueue.offer(new Task("用户通知", 4));
System.out.println("优先级队列按优先级排序:");
while (!priorityQueue.isEmpty()) {
Task task = priorityQueue.poll();
System.out.println("处理:" + task.name + " (优先级:" + task.priority + ")");
}
}
}
// 任务类,实现 Comparable 接口以支持排序
class Task implements Comparable<Task> {
String name;
int priority; // 数值越小,优先级越高
public Task(String name, int priority) {
this.name = name;
this.priority = priority;
}
@Override
public int compareTo(Task other) {
return Integer.compare(this.priority, other.priority); // 按优先级升序
}
@Override
public String toString() {
return name + "[" + priority + "]";
}
}
输出结果:
优先级队列按优先级排序:
处理:紧急修复 (优先级:1)
处理:数据同步 (优先级:2)
处理:日志轮转 (优先级:3)
处理:用户通知 (优先级:4)
💡 重点:
PriorityQueue会自动根据元素的compareTo方法排序。你需要让自定义类实现Comparable接口。
总结:Java 实例 – 队列(Queue)用法的实用指南
队列是 Java 中最常用的数据结构之一,它简单、高效,尤其适合处理“顺序处理”类问题。通过本文的学习,你应该掌握了以下核心内容:
- 队列的基本操作:
add/offer、remove/poll、peek/element - 不同实现类的适用场景:
LinkedList通用、ArrayDeque高性能、PriorityQueue优先级处理 - 实际项目中的应用:任务调度、消息队列、缓冲处理
- 安全使用建议:优先使用
offer()和poll(),避免异常 - 高级技巧:自定义对象排序,结合
Comparable构建优先级队列
无论你是刚接触 Java 的新手,还是希望夯实基础的中级开发者,掌握队列的用法,都能让你在编程路上走得更稳、更远。
下一次当你看到“排队等待”时,不妨想想:这不正是 Java 队列的完美写照吗?