Java 中的 Queue 接口是集合框架中用于表示队列的核心接口,遵循先进先出(FIFO)原则,但也支持优先级队列 PriorityQueue 或双端队列 Deque
方法签名 | 描述 |
---|---|
boolean add(E e) | 将指定元素添加到队列的尾部。若队列无法添加元素,则抛出异常 |
boolean offer(E e) | 将指定元素添加到队列的尾部。若队列无法添加元素,则返回 false |
E remove() | 移除并返回队列头部的元素。若队列为空,则抛出异常 |
E poll() | 移除并返回队列头部的元素。若队列为空,则返回 null |
E element() | 返回队列头部的元素,但不移除它。若队列为空,则抛出异常 |
E peek() | 返回队列头部的元素,但不移除它。若队列为空,则返回 null |
int size() | 返回队列中的元素数量 |
boolean isEmpty() | 判断队列是否为空 |
Java 队列有多种实现,最常使用的有三种类型
- 普通队列:实现 Queue 接口,先进先出(FIFO)队列
- 双端队列:实现 Deque 接口,在队首或者队尾都可以进行元素的插入和删除操作的队列
- 优先队列:元素按照优先级排序,对队列中删除元素时自动将优先级最高的元素出队
双端队列
双端队列头尾都可以插入、删除,使用其中的部分方法可以实现 FIFO 普通队列、LIFO 堆栈,所有双端队列都实现 Deque(Double End Queue 接口)
java
public interface Deque<E> extends Queue<E> {
// 向队首添加一个元素;如果有空间则添加成功返回true,否则则抛出异常
void addFirst(E e);
// 向队尾添加一个元素;如果有空间则添加成功返回true,否则则抛出异常
void addLast(E e);
// 向队首添加一个元素;如果有空间则添加成功返回true,否则返回false
boolean offerFirst(E e);
// 向队尾添加一个元素;如果有空间则添加成功返回true,否则返回false
boolean offerLast(E e);
// 从队首删除一个元素;如果元素存在则返回队首元素,否则抛出异常
E removeFirst();
// 从队尾删除一个元素;如果元素存在则返回队尾元素,否则抛出异常
E removeLast();
// 从队首删除一个元素;如果元素存在则返回队首元素,否则返回null
E pollFirst();
// 从队尾删除一个元素;如果元素存在则返回队首元素,否则返回null
E pollLast();
// 从队首获取一个元素,但是不删除;如果元素存在则返回队首元素,否则抛出异常
E getFirst();
// 从队尾获取一个元素,但是不删除;如果元素存在则返回队尾元素,否则抛出异常
E getLast();
// 从队首获取一个元素,但是不删除;如果元素存在则返回队首元素,否则返回null
E peekFirst();
// 从队尾获取一个元素,但是不删除;如果元素存在则返回队尾元素,否则返回null
E peekLast();
// ...
}
LinkedList
在 List 章节出现的 LinkedList 还实现了 Queue、Deque 接口,同时是一个双端队列
java
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
}
使用 LinkedList 可以模拟 LIFO 堆栈
java
import java.util.LinkedList;
public class Stack<E> {
private LinkedList<E> list;
public Stack() {
list = new LinkedList<E>();
}
public void push(E value) {
list.addFirst(value);
}
public E pop() {
return list.removeFirst();
}
public E peek() {
return list.getFirst();
}
public boolean isEmpty() {
return list.isEmpty();
}
public int size() {
return list.size();
}
}
ArrayDeque
ArrayDeque 是 Deque 接口的数组实现双端队列,也实现了 Queue 接口。ArrayDeque 基于数组实现,提供了比 LinkedList 更高效的队列和栈操作
java
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Queue;
public class ArrayDequeExample {
public static void main(String[] args) {
// 作为 Queue 使用
Queue<String> queue = new ArrayDeque<>();
queue.offer("Apple");
queue.offer("Banana");
queue.add("Cherry");
System.out.println("初始队列: " + queue); // 输出: 初始队列: [Apple, Banana, Cherry]
// 移除元素
System.out.println("移除的元素: " + queue.poll()); // 输出: 移除的元素: Apple
System.out.println("移除后队列: " + queue); // 输出: 移除后队列: [Banana, Cherry]
// 作为 Deque 使用
Deque<Integer> deque = new ArrayDeque<>();
deque.push(100);
deque.push(200);
deque.addLast(300);
System.out.println("Deque: " + deque); // 输出: Deque: [200, 100, 300]
// 弹出元素
System.out.println("弹出的元素: " + deque.pop()); // 输出: 弹出的元素: 200
System.out.println("弹出后 Deque: " + deque); // 输出: 弹出后 Deque: [100, 300]
}
}
优先级队列:PriorityQueue
优先队列基于堆实现,元素按照优先级排序,插入元素时根据指定的优先级比较方法(默认是自然顺序),确定元素优先级后插入到队列合适位置,对队列中删除元素时自动将优先级最高的元素出队
java
import java.util.Comparator;
import java.util.PriorityQueue;
public class QueueDemo {
public static void main(String[] args) {
PriorityQueue<Integer> pq = new PriorityQueue<>(new MyComparator());
pq.add(1);
pq.add(2);
pq.add(3);
pq.add(4);
pq.add(5);
pq.add(6);
while (!pq.isEmpty()) {
System.out.print(pq.poll() + " "); // 2 4 6 1 3 5
}
}
}
// 偶数优先,升序
class MyComparator implements Comparator<Integer> {
@Override
public int compare(Integer x, Integer y) {
if (x % 2 == 0 && y % 2 == 0) {
return x - y;
} else if (x % 2 == 0) {
return -1;
} else if (y % 2 == 0) {
return 1;
} else {
return x - y;
}
}
}
线程安全
前面提到的几种 Queue 的实现是非线程安全的,如果在多个线程进行入队和出队操作,将会产生数据不一致的情况。Java 提供了两种线程安全队列的实现方式
-
阻塞机制:通过使用锁的方式来实现,在入队和出队时通过加锁避免并发操作,常见的有
- ArrayBlockingQueue
- LinedBlockingQueue
- PriorityBlockingQueue
- SynchronousQueue
- DelayQueue
-
非阻塞机制:使用非阻塞算法 CAS(Compare and Swap)方式实现,无锁设计,高并发场景性能优异,典型实现是 ConcurrentLinkedQueue
java
ConcurrentLinkedQueue<String> concurrentQueue = new ConcurrentLinkedQueue<>();
// 添加元素
concurrentQueue.add("Apple");
concurrentQueue.offer("Banana");
concurrentQueue.add("Cherry");
System.out.println("初始 ConcurrentLinkedQueue: " + concurrentQueue);
// 输出: 初始 ConcurrentLinkedQueue: [Apple, Banana, Cherry]
// 移除元素
System.out.println("移除的元素: " + concurrentQueue.poll()); // 输出: 移除的元素: Apple
System.out.println("移除后 ConcurrentLinkedQueue: " + concurrentQueue);
// 输出: 移除后 ConcurrentLinkedQueue: [Banana, Cherry]
// 查看头部元素
System.out.println("ConcurrentLinkedQueue 头部元素: " + concurrentQueue.peek()); // 输出: Banana
Queue 的选择
场景 | 推荐实现类 | 理由 |
---|---|---|
单线程简单队列 | LinkedList / ArrayDeque | 轻量高效 |
按优先级处理元素 | PriorityQueue | 基于堆的优先级排序 |
高并发非阻塞队列 | ConcurrentLinkedQueue | 无锁线程安全,性能高 |
生产者-消费者模型(阻塞) | ArrayBlockingQueue | 固定容量,支持公平锁 |
无界生产者-消费者模型 | LinkedBlockingQueue | 默认无界,吞吐量高 |
需要双端队列操作 | ArrayDeque | 循环数组实现,性能优于 LinkedList |