前端视角 Java Web 入门手册 2.4.3:集合框架——Queue

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
相关推荐
一只叫煤球的猫3 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz9653 小时前
tcp/ip 中的多路复用
后端
bobz9653 小时前
tls ingress 简单记录
后端
皮皮林5514 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
你的人类朋友5 小时前
什么是OpenSSL
后端·安全·程序员
bobz9655 小时前
mcp 直接操作浏览器
后端
前端小张同学7 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook7 小时前
Manim实现闪光轨迹特效
后端·python·动效
武子康8 小时前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark
该用户已不存在8 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net