队列与queue

前言

这篇文章来和大家分享一下java集合框架与queue的使用.老话说,读书百遍其义自见.这是我第二次学习一些新的心得与理解.


一、java集合框架

  • Java 集合框架是 Java 中用于存储和操作一组对象的体系,核心分为 Collection(单列集合)和Map(双列集合)

核心接口与分类

  • Collection(单列集合)
    • 是所有单列集合的根接口,定义了集合的基本操作(增删改查、遍历等)。
    • 子接口:List(有序可重复)、Set(无序不可重复)、Queue(队列)。
  • Map(双列集合)
    • 存储键值对(Key-Value),Key 唯一、Value 可重复。
    • 子接口:SortedMap(键有序)。

queue

  • 在 Java 集合框架中,Queue(队列) 是继承自Collection的子接口,用于实现先进先出(FIFO) 的数据结构(部分实现类会打破 FIFO,如优先级队列)。它是处理 "按顺序排队" 场景的核心工具,比如任务调度、消息队列等。
  • 一、核心特性
    FIFO 原则:默认先入队的元素会先出队(部分实现类如PriorityQueue按优先级排序)。
  • 双组操作方法:提供 "抛异常" 和 "返回特殊值" 两套方法,适配不同场景(如下表)。
  • 可扩展:支持有界 / 无界队列,部分实现(如阻塞队列)支持多线程并发。

由于queue实现了linkedlist和arraylist的接口,可以使用他们两个来实现,不过一般是使用链表进行实现,下面也会手动实现一下~~

二、队列

  • 在数据结构中,顺序表是用连续的存储单元存储元素的线性表,而 Java 中的ArrayList就是顺序表的具体实现,下面从数据结构方面介绍:

基本解释

  • 队列(Queue)是一种先进先出 (FIFO, First In First Out) 的线性数据结构,它的操作遵循 "先进入队列的元素,先出队列" 的规则,就像日常生活中排队买票、排队结账的场景。

核心操作

队列的操作主要围绕队首(出队端)和队尾(入队端)展开,基本操作分为 4 类:

  • 入队(Enqueue):在队尾添加一个元素。
  • 出队(Dequeue):从队首移除并返回一个元素。
  • 查看队首(Peek/Front):返回队首元素,但不移除它。
  • 判空 / 判满:判断队列是否为空,或是否已满(仅针对有界队列)。

队列的分类

根据不同的标准,队列可以分为多种类型:

我们主要是看一下按存储结构分

  • 顺序队列:基于数组实现,通常会设计成循环队列来解决 "假溢出" 问题(即队尾到达数组末尾,但队首前方还有空闲空间)。
  • 链式队列:基于链表实现,没有固定容量限制,元素入队时直接在链表尾部追加节点,出队时删除链表头部节点。
    三、典型应用场景
    任务调度:比如操作系统的进程调度、打印机的任务排队。
    消息队列:分布式系统中,用于解耦生产者和消费者,削峰填谷。
    广度优先搜索(BFS):算法中常用队列存储待访问的节点,逐层遍历图或树。
    缓存淘汰:部分缓存策略(如 FIFO 缓存)基于队列实现。

三、常用代码手动实现

  • 这一部分的逻辑是较为简单的,小伙伴们如果是第一次接触,非常建议大家上手实现一下~
    我就都分成一个一个小的代码块了 大家在学习的时候也可以分成基本成员变量 ,成员方法,**辅助方法(在成员方法中被调用的小方法)**进行学习

基本方法

java 复制代码
// 节点类:存储数据和下一个节点的引用
class Node<E> {
    E data;
    Node<E> next;

    public Node(E data) {
        this.data = data;
        this.next = null;
    }
}

// 链表队列
public class LinkedQueue<E> {
    // 队首指针(指向头节点)、队尾指针(指向尾节点)
    private Node<E> front;
    private Node<E> rear;
    // 队列元素个数
    private int size;

    // 构造方法:初始化空队列
    public LinkedQueue() {
        this.front = null;
        this.rear = null;
        this.size = 0;
    }

    /**
     * 入队:队尾添加元素
     * @param e 要添加的元素
     * @return 永远返回true(链表无界)
     */
    public boolean offer(E e) {
        Node<E> newNode = new Node<>(e);
        if (isEmpty()) {
            // 空队列时,头尾指针都指向新节点
            front = newNode;
            rear = newNode;
        } else {
            // 非空队列时,尾节点的next指向新节点,更新尾指针
            rear.next = newNode;
            rear = newNode;
        }
        size++;
        return true;
    }

    /**
     * 出队:移除并返回队首元素
     * @return 队首元素,队空返回null
     */
    public E poll() {
        if (isEmpty()) {
            return null;
        }
        // 取出队首数据
        E value = front.data;
        // 头指针后移
        front = front.next;
        size--;
        // 如果队列为空,尾指针也要置空
        if (isEmpty()) {
            rear = null;
        }
        return value;
    }

    /**
     * 查看队首元素(不移除)
     * @return 队首元素,队空返回null
     */
    public E peek() {
        if (isEmpty()) {
            return null;
        }
        return front.data;
    }

    // 判断队列是否为空
    public boolean isEmpty() {
        return size == 0;
    }

    // 获取队列元素个数
    public int size() {
        return size;
    }

四、顺序表实现队列(环形队列)

  • 上面我们是用链表的形式实现的,数组实现也是重要的形式,有着高效的查找能力等优点,不过我们要想通过顺序表的形式进行面临着两个直观的问题.

  • 第一

    一个数组在物理上不是圆形的,当一个存在元素的队列出队时,就会存在空间的浪费,我们在入队是不是简单的下表+1了.

    实际上我们可以 "取模运算" 让下标 "循环移动",把数组 "逻辑上变成环形",避免出队后的空间浪费。比如

    • 初始时front=0,rear=0(队空)。
    • 入队 3 个元素后,rear=3(元素存在0、1、2下标)。
    • 出队 1 个元素,front=1(此时0下标空了)。
    • 再入队时,rear = (3+1) % 4 = 0(把新元素存到0下标,复用了出队空出的空间)。
  • 第二

    既然是环形的那怎样是满了呢,或者说如何标记队列满了?

    实际上循环队列通过 "牺牲 1 个数组空间" 来区分 "队空" 和 "队满":

    • 队空条件:front == rear(头尾指针重合)。
    • 队满条件:(rear + 1) % 数组长度 == front(尾指针的 "下一个位置" 是头指针)。

    代码实现

java 复制代码
public class CircularQueue<E> {
    private final Object[] arr;
    private int front; // 队首指针
    private int rear;  // 队尾指针
    private final int len; // 数组长度(=实际可存元素数+1)

    public CircularQueue(int maxSize) {
        this.len = maxSize + 1; // 牺牲1个空间
        this.arr = new Object[len];
        this.front = 0;
        this.rear = 0;
    }

    // 入队:复用空间(取模实现环形)
    public boolean offer(E e) {
        if ((rear + 1) % len == front) {
            return false; // 队满
        }
        arr[rear] = e;
        rear = (rear + 1) % len; // 循环移动下标
        return true;
    }

    // 出队
    @SuppressWarnings("unchecked")
    public E poll() {
        if (front == rear) {
            return null; // 队空
        }
        E value = (E) arr[front];
        front = (front + 1) % len; // 循环移动下标
        return value;
    }
}

总结

  • 到这里我的分享就先结束了~,希望对你有帮助
  • 我是dylan 下次见~
    • 无限进步
相关推荐
それども11 分钟前
什么是MalformedStreamException,和WebKitFormBoundary有什么关系
java
wWYy.23 分钟前
指针与引用区别
数据结构
思想在飞肢体在追36 分钟前
Springboot项目配置Nacos
java·spring boot·后端·nacos
cyforkk38 分钟前
09、Java 基础硬核复习:异常处理(容错机制)的核心逻辑与面试考点
java·数据库·面试
历程里程碑40 分钟前
Linux 17 程序地址空间
linux·运维·服务器·开发语言·数据结构·笔记·排序算法
??(lxy)1 小时前
java高性能无锁队列——MpscLinkedQueue
java·开发语言
数研小生1 小时前
Full Analysis of Taobao Item Detail API taobao.item.get
java·服务器·前端
-dzk-1 小时前
【代码随想录】LC 203.移除链表元素
c语言·数据结构·c++·算法·链表
齐落山大勇2 小时前
数据结构——栈与队列
数据结构
Wang15302 小时前
Java编程基础与面向对象核心概念
java