Java 实例 – 队列(Queue)用法(完整教程)

Java 实例 – 队列(Queue)用法:从入门到实战

你有没有在餐厅等位时排队的经历?前一个人点完餐,后一个人才能轮到。这种“先来先服务”的机制,就是队列(Queue)在生活中的真实写照。在 Java 编程中,队列是一种非常基础且重要的数据结构,它遵循“先进先出”(FIFO, First In First Out)的原则,广泛应用于任务调度、消息传递、缓冲处理等场景。

今天我们就来深入聊聊 Java 实例 – 队列(Queue)用法,通过实际代码和案例,带你一步步掌握它的核心用法。无论是初学者还是有一定经验的开发者,都能从中收获实用技巧。


什么是队列?它为什么重要?

队列是一种只能在一端插入(入队,enqueue),另一端删除(出队,dequeue)的线性数据结构。你可以把它想象成一条单行道的收费通道:车从入口进入,必须按顺序从出口离开,不能插队。

在 Java 中,java.util.Queue 是一个接口,定义了队列的基本操作。它的实现类有很多,比如 LinkedListPriorityQueueArrayDeque 等。每种实现适用于不同场景,我们稍后会一一讲解。

⚠️ 注意: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 / offerremove / pollpeek / element
  • 不同实现类的适用场景:LinkedList 通用、ArrayDeque 高性能、PriorityQueue 优先级处理
  • 实际项目中的应用:任务调度、消息队列、缓冲处理
  • 安全使用建议:优先使用 offer()poll(),避免异常
  • 高级技巧:自定义对象排序,结合 Comparable 构建优先级队列

无论你是刚接触 Java 的新手,还是希望夯实基础的中级开发者,掌握队列的用法,都能让你在编程路上走得更稳、更远。

下一次当你看到“排队等待”时,不妨想想:这不正是 Java 队列的完美写照吗?