教程中心 / Java教程 / 队列

队列

12分钟 集合框架

什么是队列?

队列(Queue)是一种先进先出(FIFO - First In First Out)的数据结构。想象你在餐厅排队取餐——第一个排队的人第一个拿到食物。队列就是这样一种"先来先服务"的数据结构。

生活比喻

队列就像节假日高速公路收费站的人工通道。车辆从一端进入(队尾),从另一端离开(队首)。每个经过的司机都按照到达的先后顺序依次通过,先到的先走,后到的排队等候。

队列示意图:
    
    入队 (enqueue)                    出队 (dequeue)
        ↓                               ↓
    ┌───┬───┬───┬───┬───┐
    │ A │ B │ C │ D │   │   →  队首元素先出
    └───┴───┴───┴───┴───┘
     ↑                   ↑
    队首                队尾
    (front)             (rear)

Queue接口的方法分类

Queue接口的方法分为三类,每类都有两种形式——一种在操作失败时抛出异常,另一种返回特殊值(false或null):

操作类型抛异常返回特殊值说明
入队add(e)offer(e)添加到队尾
区别:队满时add抛异常,offer返回false
出队remove()poll()移除并返回队首
区别:空队列时remove抛异常,poll返回null
查看element()peek()查看队首元素
区别:空队列时element抛异常,peek返回null

最佳实践:通常推荐使用offer()、poll()、peek()方法,因为它们的行为更可预测,不会因为队列状态而抛出异常。

Queue的实现类

LinkedList

最常用的Queue实现,既可以用作队列也可以用作列表

ArrayDeque

基于数组实现的双端队列,比LinkedList效率更高

PriorityQueue

优先级队列,出队顺序由优先级决定而非FIFO

LinkedList实现Queue

import java.util.LinkedList;
import java.util.Queue;

public class QueueDemo {
    public static void main(String[] args) {
        // 创建队列
        Queue queue = new LinkedList<>();

        // 入队操作
        queue.offer("第一位客户");
        queue.offer("第二位客户");
        queue.offer("第三位客户");
        System.out.println("入队后: " + queue);

        // 查看队首(不移除)
        System.out.println("队首元素: " + queue.peek());

        // 出队操作
        String first = queue.poll();
        System.out.println("出队: " + first);
        System.out.println("出队后: " + queue);

        // 继续出队
        queue.poll();
        queue.poll();
        System.out.println("全部出队后: " + queue);

        // 尝试对空队列操作
        System.out.println("空队列peek: " + queue.peek());  // null
        System.out.println("空队列poll: " + queue.poll()); // null
    }
}

ArrayDeque双端队列

Deque(Double Ended Queue)是Queue的子接口,支持在两端进行插入和删除操作。ArrayDeque是Deque的高效实现。

双端队列示意图:
    
    ←←← 两端都可以进出 →→→
    
    ┌───┬───┬───┬───┬───┐
    │ A │ B │ C │ D │ E │
    └───┴───┴───┴───┴───┘
     ↑                   ↑
    首                  尾
    可addFirst/removeFirst  可addLast/removeLast
import java.util.ArrayDeque;
import java.util.Deque;

public class ArrayDequeDemo {
    public static void main(String[] args) {
        Deque deque = new ArrayDeque<>();

        // 从队尾添加(普通队列操作)
        deque.offerLast("A");
        deque.offerLast("B");
        deque.offerLast("C");

        // 从队首添加
        deque.offerFirst("Start");

        System.out.println("deque: " + deque);  // [Start, A, B, C]

        // 查看
        System.out.println("队首: " + deque.peekFirst());  // Start
        System.out.println("队尾: " + deque.peekLast());   // C

        // 从不同端移除
        deque.removeFirst();
        deque.removeLast();
        System.out.println("移除首尾后: " + deque);  // [A, B]
    }
}

PriorityQueue优先级队列

PriorityQueue是一种特殊的队列,它的出队顺序不是按照入队先后,而是按照元素的优先级。默认情况下,优先级由元素的自然顺序决定(最小元素先出队)。

生活比喻

PriorityQueue就像医院的急诊室。虽然按顺序挂号,但医生会根据病情的严重程度来决定谁先就诊。严重的病人优先级高,会被优先处理。

import java.util.PriorityQueue;
import java.util.Collections;

public class PriorityQueueDemo {
    public static void main(String[] args) {
        // 默认是最小堆(最小元素先出)
        PriorityQueue minHeap = new PriorityQueue<>();
        minHeap.add(30);
        minHeap.add(10);
        minHeap.add(50);
        minHeap.add(20);

        System.out.println("PriorityQueue: " + minHeap);
        System.out.println("peek (最小): " + minHeap.peek());
        System.out.println("poll: " + minHeap.poll());  // 10
        System.out.println("poll: " + minHeap.poll());  // 20

        System.out.println("\n===== 最大堆 =====");
        // 创建最大堆(最大元素先出)
        PriorityQueue maxHeap = new PriorityQueue<>(Collections.reverseOrder());
        maxHeap.addAll(java.util.Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6));

        System.out.println("peek (最大): " + maxHeap.peek());
        while (!maxHeap.isEmpty()) {
            System.out.print(maxHeap.poll() + " ");
        }
    }
}

PriorityQueue自定义优先级

import java.util.PriorityQueue;

class Task {
    private String name;
    private int priority;  // 数字越小优先级越高

    public Task(String name, int priority) {
        this.name = name;
        this.priority = priority;
    }

    public int getPriority() { return priority; }
    public String getName() { return name; }

    @Override
    public String toString() {
        return name + "(优先级:" + priority + ")";
    }
}

public class CustomPriorityQueue {
    public static void main(String[] args) {
        // 使用自定义比较器:优先级数字越小越先出
        PriorityQueue taskQueue = new PriorityQueue<>(
            (t1, t2) -> t1.getPriority() - t2.getPriority()
        );

        taskQueue.offer(new Task("紧急Bug修复", 1));
        taskQueue.offer(new Task("优化数据库", 3));
        taskQueue.offer(new Task("写文档", 5));
        taskQueue.offer(new Task("回复邮件", 2));

        System.out.println("任务执行顺序:");
        while (!taskQueue.isEmpty()) {
            Task task = taskQueue.poll();
            System.out.println("执行: " + task);
        }
    }
}

队列的使用场景

1

任务调度

按顺序处理任务,如打印队列、消息队列

// 模拟打印机任务队列
Queue printQueue = new LinkedList<>();
printQueue.offer(new PrintJob("文档1"));
printQueue.offer(new PrintJob("文档2"));
// 打印机按顺序处理
2

广度优先搜索(BFS)

图和树的层次遍历

Queue queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
    Node node = queue.poll();
    // 处理node
    queue.offer(node.left);
    queue.offer(node.right);
}
3

生产者-消费者模式

解耦生产者和消费者处理速度

Queue<消息> channel = new LinkedList<>();
// 生产者: channel.offer(message);
// 消费者: channel.poll();

三种队列实现对比

特性LinkedListArrayDequePriorityQueue
底层实现双向链表循环数组堆(数组实现)
操作复杂度O(1)O(1)O(log n)
是否FIFO否(按优先级)
null元素允许不允许不允许
线程安全
适用场景通用队列、列表高效双端队列优先级任务调度

常见错误和注意事项

错误1:在队空时调用remove()和element()

空队列调用remove()会抛出NoSuchElementException,而poll()会返回null。如果你不确定队列是否为空,先用peek()检查。

错误2:PriorityQueue不允许null

PriorityQueue不允许存储null元素,因为它需要比较元素大小。如果添加null,会抛出NullPointerException。

性能提示:如果需要使用双端队列操作(两端都能添加/删除),使用ArrayDeque而不是LinkedList。ArrayDeque的数组实现比LinkedList的链表实现有更好的缓存局部性。

小结

  • Queue是先进先出(FIFO)的数据结构
  • offer/poll/peek是推荐的方法,不会抛异常
  • LinkedList是最常用的Queue实现,可以作为FIFO队列使用
  • ArrayDeque是高效的双端队列实现,适合Stack和Queue场景
  • PriorityQueue是优先级队列,按优先级而非FIFO出队
  • 选择合适的队列类型取决于你的具体需求
☕ Java 在线代码编辑器
📝 运行结果