Skip to content

Queue接口详解

Queue在集合框架中的位置

在深入了解Queue之前,我们先来看看它在Java集合框架中的位置,以及与Collection、List、Map等接口的关系。

集合框架整体架构

mermaid
graph TB
    A["Iterable接口"]
    B["Collection接口"]
    C["List接口"]
    D["Set接口"]
    E["Queue接口"]
    F["Map接口<br/>独立体系"]
    
    A --> B
    B --> C
    B --> D
    B --> E
    
    C1["ArrayList"]
    C2["LinkedList"]
    
    E1["PriorityQueue"]
    E2["ArrayDeque"]
    E3["LinkedList"]
    E4["BlockingQueue"]
    
    C --> C1
    C --> C2
    
    E --> E1
    E --> E2
    E --> E3
    E --> E4
    
    style A fill:#90EE90
    style B fill:#87CEEB
    style C fill:#FFB6C1
    style D fill:#DDA0DD
    style E fill:#FF6B6B
    style F fill:#FFA07A

Queue与Collection的关系

Queue是Collection接口的子接口,这意味着:

  1. 继承关系: Queue继承了Collection的所有方法(add、remove、size、isEmpty等)
  2. 特殊语义: Queue在此基础上定义了队列特有的操作(offer、poll、peek等)
  3. 遍历支持: 作为Collection的子类,Queue同样支持Iterator遍历
java
// Queue继承自Collection,可以使用Collection的方法
Queue<String> queue = new LinkedList<>();
queue.add("元素1");        // Collection方法
queue.offer("元素2");      // Queue特有方法
queue.size();              // Collection方法
queue.isEmpty();           // Collection方法

// 遍历队列
for (String item : queue) {
    System.out.println(item);
}

Queue与List的区别

虽然Queue和List都继承自Collection,但它们的设计目标完全不同:

mermaid
graph LR
    A["Collection接口"]
    B["List接口"]
    C["Queue接口"]
    
    B1["有序集合<br/>支持索引访问<br/>允许重复元素"]
    C1["队列语义<br/>FIFO/优先级<br/>不支持索引访问"]
    
    A --> B
    A --> C
    B --> B1
    C --> C1
    
    style A fill:#87CEEB
    style B fill:#98FB98
    style C fill:#FF6B6B
特性ListQueue
设计目的有序存储,随机访问按规则处理元素
索引访问支持 get(index)不支持
元素顺序插入顺序FIFO/优先级/LIFO
典型操作add, get, set, removeoffer, poll, peek
使用场景数据存储任务调度、消息传递

Queue与Map的关系

Queue和Map是Java集合框架中的两个完全独立的体系:

mermaid
graph TB
    A["Java集合框架"]
    B["Collection体系<br/>存储单一元素"]
    C["Map体系<br/>存储键值对"]
    
    B1["List"]
    B2["Set"]
    B3["Queue"]
    
    C1["HashMap"]
    C2["TreeMap"]
    C3["LinkedHashMap"]
    
    A --> B
    A --> C
    
    B --> B1
    B --> B2
    B --> B3
    
    C --> C1
    C --> C2
    C --> C3
    
    style A fill:#87CEEB
    style B fill:#98FB98
    style C fill:#FFB6C1
    style B3 fill:#FF6B6B
  • Collection体系: 包括List、Set、Queue,存储单一元素
  • Map体系: 存储键值对,与Collection没有继承关系
  • LinkedList特例: 同时实现了List和Deque(Queue的子接口),是两个接口的桥梁
java
// LinkedList同时实现List和Queue
LinkedList<String> linkedList = new LinkedList<>();

// 作为List使用
linkedList.add(0, "索引0");
linkedList.get(0);

// 作为Queue使用
linkedList.offer("入队");
linkedList.poll();

Queue接口基础

Queue的核心特点

Queue(队列)是一种特殊的线性数据结构,通常遵循**FIFO(First In First Out,先进先出)**原则。就像排队买票一样,先排队的人先买到票。

mermaid
graph LR
    A["入队<br/>offer/add"] --> B[队尾]
    B --> C["元素2"]
    C --> D["元素1"]
    D --> E[队首]
    E --> F["出队<br/>poll/remove"]
    
    style A fill:#98FB98
    style B fill:#87CEEB
    style E fill:#87CEEB
    style F fill:#FFB6C1

Queue的两组方法

Queue接口提供了两组操作方法,区别在于操作失败时的行为:

操作类型抛出异常返回特殊值说明
插入队尾add(e)offer(e)offer返回false表示失败
删除队首remove()poll()poll返回null表示队列为空
查询队首element()peek()peek返回null表示队列为空

使用建议: 在实际开发中,推荐使用 offer/poll/peek 方法,可以优雅地处理边界情况。

java
Queue<String> queue = new LinkedList<>();

// 推荐方式:使用返回特殊值的方法
queue.offer("任务1");
queue.offer("任务2");
queue.offer("任务3");

// 安全地获取队首元素
String head = queue.peek();  // 不删除,可能返回null
if (head != null) {
    System.out.println("队首: " + head);
}

// 安全地出队
String task;
while ((task = queue.poll()) != null) {
    System.out.println("处理任务: " + task);
}

Deque双端队列

Queue vs Deque

Deque(Double Ended Queue,双端队列)是Queue的子接口,允许在队列两端进行插入和删除操作。

mermaid
graph LR
    A["队首操作<br/>addFirst/removeFirst"] <--> B[队首]
    B --> C["元素2"]
    C --> D["元素3"]
    D --> E[队尾]
    E <--> F["队尾操作<br/>addLast/removeLast"]
    
    style A fill:#98FB98
    style B fill:#87CEEB
    style E fill:#87CEEB
    style F fill:#FFB6C1

Deque的方法汇总:

操作抛出异常返回特殊值
插入队首addFirst(e)offerFirst(e)
插入队尾addLast(e)offerLast(e)
删除队首removeFirst()pollFirst()
删除队尾removeLast()pollLast()
查询队首getFirst()peekFirst()
查询队尾getLast()peekLast()

Deque实现栈

由于Deque可以在两端操作,因此可以完美模拟栈(Stack)的后进先出(LIFO)行为:

mermaid
graph TB
    A["Deque模拟栈"]
    B["push = addFirst<br/>压栈"]
    C["pop = removeFirst<br/>弹栈"]
    D["peek = peekFirst<br/>查看栈顶"]
    
    A --> B
    A --> C
    A --> D
    
    style A fill:#87CEEB
    style B fill:#98FB98
    style C fill:#FFB6C1
    style D fill:#DDA0DD
java
// 使用Deque替代Stack(官方推荐)
Deque<String> stack = new ArrayDeque<>();

// 压栈
stack.push("第一层");
stack.push("第二层");
stack.push("第三层");

System.out.println("栈状态: " + stack); // [第三层, 第二层, 第一层]

// 弹栈
System.out.println(stack.pop()); // 第三层
System.out.println(stack.pop()); // 第二层
System.out.println(stack.pop()); // 第一层

为什么推荐用Deque替代Stack?

  1. Stack继承自Vector,设计过时
  2. Stack的方法都是synchronized,性能较差
  3. Deque接口更规范,实现类选择更多

ArrayDeque vs LinkedList

ArrayDeque和LinkedList都实现了Deque接口,但底层实现和性能特点不同:

mermaid
graph TB
    A["Deque实现类"]
    B["ArrayDeque<br/>循环数组"]
    C["LinkedList<br/>双向链表"]
    
    B1["内存连续<br/>缓存友好<br/>性能更好"]
    C1["内存分散<br/>支持null<br/>实现List接口"]
    
    A --> B
    A --> C
    B --> B1
    C --> C1
    
    style A fill:#87CEEB
    style B fill:#98FB98
    style C fill:#FFB6C1
特性ArrayDequeLinkedList
底层结构可变长数组+双指针双向链表
null值不支持支持
引入版本JDK 1.6JDK 1.2
扩容需要扩容(2倍)不需要
内存分配连续内存,缓存友好每次申请堆空间
性能更快较慢
实现接口仅DequeList + Deque

最佳实践: 优先使用ArrayDeque实现队列和栈,除非需要存储null值或需要List接口的功能。

java
// 推荐:使用ArrayDeque作为队列
Deque<Integer> queue = new ArrayDeque<>();
queue.offer(1);
queue.offer(2);
queue.offer(3);

// 推荐:使用ArrayDeque作为栈
Deque<Integer> stack = new ArrayDeque<>();
stack.push(1);
stack.push(2);
stack.push(3);

PriorityQueue优先队列

优先队列原理

PriorityQueue是一个基于优先级堆的无界队列,元素按照自然顺序或自定义Comparator排序。

mermaid
graph TB
    A["10<br/>堆顶-最小值"]
    B["20"]
    C["30"]
    D["40"]
    E["50"]
    F["60"]
    G["70"]
    
    A --> B
    A --> C
    B --> D
    B --> E
    C --> F
    C --> G
    
    style A fill:#FF6B6B
    style B fill:#98FB98
    style C fill:#98FB98
    style D fill:#87CEEB
    style E fill:#87CEEB
    style F fill:#87CEEB
    style G fill:#87CEEB

PriorityQueue核心特点

特点说明
底层实现二叉堆(数组存储)
默认排序小顶堆(最小值优先)
插入/删除O(log n)
查看堆顶O(1)
线程安全
null值不支持
non-comparable不支持

使用示例

java
// 示例1:默认小顶堆
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
minHeap.offer(50);
minHeap.offer(20);
minHeap.offer(80);
minHeap.offer(10);
minHeap.offer(30);

System.out.println("小顶堆依次取出:");
while (!minHeap.isEmpty()) {
    System.out.print(minHeap.poll() + " "); // 10 20 30 50 80
}

// 示例2:大顶堆(通过Comparator)
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a);
maxHeap.offer(50);
maxHeap.offer(20);
maxHeap.offer(80);
maxHeap.offer(10);
maxHeap.offer(30);

System.out.println("\n大顶堆依次取出:");
while (!maxHeap.isEmpty()) {
    System.out.print(maxHeap.poll() + " "); // 80 50 30 20 10
}

自定义对象排序

java
// 定义任务类
class Task {
    String name;
    int priority;  // 数字越小优先级越高
    
    public Task(String name, int priority) {
        this.name = name;
        this.priority = priority;
    }
    
    @Override
    public String toString() {
        return name + "(优先级:" + priority + ")";
    }
}

// 使用优先队列调度任务
PriorityQueue<Task> taskQueue = new PriorityQueue<>(
    Comparator.comparingInt(t -> t.priority)
);

taskQueue.offer(new Task("普通任务", 3));
taskQueue.offer(new Task("紧急任务", 1));
taskQueue.offer(new Task("一般任务", 2));

System.out.println("按优先级执行任务:");
while (!taskQueue.isEmpty()) {
    System.out.println("执行: " + taskQueue.poll());
}
// 输出:
// 执行: 紧急任务(优先级:1)
// 执行: 一般任务(优先级:2)
// 执行: 普通任务(优先级:3)

应用场景

mermaid
graph TB
    A["PriorityQueue应用场景"]
    B["任务调度<br/>高优先级先执行"]
    C["Top K问题<br/>找最大/最小K个元素"]
    D["合并有序列表<br/>多路归并"]
    E["Dijkstra算法<br/>最短路径"]
    
    A --> B
    A --> C
    A --> D
    A --> E
    
    style A fill:#87CEEB
    style B fill:#98FB98
    style C fill:#FFB6C1
    style D fill:#DDA0DD
    style E fill:#FFA07A

BlockingQueue阻塞队列

阻塞队列概述

BlockingQueue是Queue的子接口,专为多线程并发场景设计,支持阻塞操作。

mermaid
graph TB
    A["BlockingQueue接口"]
    B["ArrayBlockingQueue<br/>数组实现<br/>有界"]
    C["LinkedBlockingQueue<br/>链表实现<br/>可选有界"]
    D["PriorityBlockingQueue<br/>优先级<br/>无界"]
    E["SynchronousQueue<br/>不存储元素<br/>直接传递"]
    F["DelayQueue<br/>延迟队列<br/>无界"]
    
    A --> B
    A --> C
    A --> D
    A --> E
    A --> F
    
    style A fill:#87CEEB
    style B fill:#98FB98
    style C fill:#FFB6C1
    style D fill:#DDA0DD
    style E fill:#FFA07A
    style F fill:#90EE90

阻塞队列的核心特性

特性说明
队列空时获取操作阻塞,直到有元素
队列满时插入操作阻塞,直到有空间
线程安全内置同步机制
典型应用生产者-消费者模型

阻塞队列的四组方法

操作抛出异常返回特殊值阻塞超时
插入add(e)offer(e)put(e)offer(e, time, unit)
删除remove()poll()take()poll(time, unit)
查看element()peek()--

ArrayBlockingQueue vs LinkedBlockingQueue

mermaid
graph LR
    A["阻塞队列对比"]
    B["ArrayBlockingQueue"]
    C["LinkedBlockingQueue"]
    
    B1["数组实现<br/>必须指定容量<br/>单锁机制"]
    C1["链表实现<br/>可选容量<br/>双锁机制"]
    
    A --> B
    A --> C
    B --> B1
    C --> C1
    
    style A fill:#87CEEB
    style B fill:#98FB98
    style C fill:#FFB6C1
特性ArrayBlockingQueueLinkedBlockingQueue
底层实现数组链表
是否有界必须指定容量可选(默认Integer.MAX_VALUE)
锁机制单锁(生产消费共用)双锁(读写分离)
内存占用预分配动态分配
吞吐量锁竞争大锁竞争小,吞吐量更高

生产者-消费者示例

java
public class ProducerConsumerDemo {
    public static void main(String[] args) {
        // 创建容量为5的阻塞队列
        BlockingQueue<String> queue = new ArrayBlockingQueue<>(5);
        
        // 生产者线程
        Thread producer = new Thread(() -> {
            try {
                for (int i = 1; i <= 10; i++) {
                    String product = "产品" + i;
                    queue.put(product);  // 队列满时阻塞
                    System.out.println("生产: " + product);
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "生产者");
        
        // 消费者线程
        Thread consumer = new Thread(() -> {
            try {
                for (int i = 1; i <= 10; i++) {
                    String product = queue.take();  // 队列空时阻塞
                    System.out.println("消费: " + product);
                    Thread.sleep(200);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "消费者");
        
        producer.start();
        consumer.start();
    }
}

DelayQueue延迟队列

DelayQueue是一个特殊的阻塞队列,元素只有在延迟期满后才能被取出。

java
// 定义延迟任务
class DelayedTask implements Delayed {
    private String name;
    private long executeTime;  // 执行时间
    
    public DelayedTask(String name, long delayMs) {
        this.name = name;
        this.executeTime = System.currentTimeMillis() + delayMs;
    }
    
    @Override
    public long getDelay(TimeUnit unit) {
        long diff = executeTime - System.currentTimeMillis();
        return unit.convert(diff, TimeUnit.MILLISECONDS);
    }
    
    @Override
    public int compareTo(Delayed other) {
        return Long.compare(this.executeTime, 
            ((DelayedTask) other).executeTime);
    }
    
    @Override
    public String toString() {
        return name;
    }
}

// 使用延迟队列
DelayQueue<DelayedTask> delayQueue = new DelayQueue<>();
delayQueue.offer(new DelayedTask("任务A", 3000));  // 3秒后执行
delayQueue.offer(new DelayedTask("任务B", 1000));  // 1秒后执行
delayQueue.offer(new DelayedTask("任务C", 2000));  // 2秒后执行

System.out.println("开始取任务...");
while (!delayQueue.isEmpty()) {
    DelayedTask task = delayQueue.take();  // 阻塞直到延迟期满
    System.out.println("执行: " + task + " at " + System.currentTimeMillis());
}
// 输出顺序: 任务B -> 任务C -> 任务A

DelayQueue应用场景:

  • 订单超时取消
  • 缓存过期清理
  • 任务定时执行
  • 会话超时管理

Queue的选择指南

根据场景选择Queue实现

mermaid
graph TD
    A["选择Queue实现"]
    B{"是否需要阻塞?"}
    C{"是否需要优先级?"}
    D{"是否需要双端操作?"}
    E{"是否需要延迟?"}
    
    F["ArrayDeque"]
    G["PriorityQueue"]
    H["ArrayBlockingQueue"]
    I["LinkedBlockingQueue"]
    J["PriorityBlockingQueue"]
    K["DelayQueue"]
    
    A --> B
    B -- 否 --> C
    B -- 是 --> E
    
    C -- 是 --> G
    C -- 否 --> D
    D -- 是 --> F
    D -- 否 --> F
    
    E -- 是 --> K
    E -- 否 --> H
    
    style A fill:#87CEEB
    style F fill:#98FB98
    style G fill:#FFB6C1
    style H fill:#DDA0DD
    style K fill:#FFA07A

选择建议总结

使用场景推荐实现理由
普通队列ArrayDeque性能最佳,内存友好
栈结构ArrayDeque替代过时的Stack类
优先级队列PriorityQueue基于堆,O(log n)操作
生产者-消费者LinkedBlockingQueue双锁设计,吞吐量高
有界阻塞队列ArrayBlockingQueue防止内存溢出
定时任务DelayQueue延迟期满才能取出
线程间直接传递SynchronousQueue不存储元素

总结

Queue核心知识点

mermaid
graph TB
    A["Queue知识体系"]
    B["基础概念"]
    C["主要实现"]
    D["阻塞队列"]
    E["使用场景"]
    
    B1["FIFO原则"]
    B2["两组API"]
    B3["Deque扩展"]
    
    C1["ArrayDeque"]
    C2["PriorityQueue"]
    C3["LinkedList"]
    
    D1["ArrayBlockingQueue"]
    D2["LinkedBlockingQueue"]
    D3["DelayQueue"]
    
    E1["任务调度"]
    E2["消息队列"]
    E3["生产者-消费者"]
    
    A --> B
    A --> C
    A --> D
    A --> E
    
    B --> B1
    B --> B2
    B --> B3
    
    C --> C1
    C --> C2
    C --> C3
    
    D --> D1
    D --> D2
    D --> D3
    
    E --> E1
    E --> E2
    E --> E3
    
    style A fill:#87CEEB
    style B fill:#98FB98
    style C fill:#FFB6C1
    style D fill:#DDA0DD
    style E fill:#FFA07A

面试要点

  1. Queue与Deque的区别: Queue是单端队列,Deque是双端队列
  2. 为什么用ArrayDeque替代Stack: Stack继承Vector,设计过时,性能差
  3. PriorityQueue的原理: 基于二叉堆,默认小顶堆
  4. BlockingQueue的作用: 支持阻塞操作,用于生产者-消费者模型
  5. ArrayBlockingQueue vs LinkedBlockingQueue: 单锁vs双锁,有界vs可选有界

更新: 2025-12-07 10:12:42
原文: https://www.yuque.com/u22210564/zoxfmt/pxpfi11thww26zsk

Java 后端面试知识库