Java集合框架-Collection-queue

news2024/9/21 14:40:04

目录

    • 一、Deque
    • 二、ArrayDeque
      • ArrayDeque层次结构图
      • ArrayDeque概述
      • ArrayDeque底层数据结构
      • ArrayDeque常用方法(简略)
    • 三、PriorityQueue
      • PriorityQueue层次结构图
      • PriorityQueue概述
      • PriorityQueue 底层数据结构
      • PriorityQueue常用方法(详细)

在这里插入图片描述

  • Java里有一个叫做Stack的类,却没有叫做Queue的类(它是个接口名字)。当需要使用栈时,Java已不推荐使用Stack,而是推荐使用更高效的ArrayDeque;既然Queue只是一个接口,当需要使用队列时也就首选ArrayDeque了(次选是LinkedList)。

一、Deque

  • Deque是"double ended queue", 表示双向的队列。 Deque 继承自 Queue接口,除了支持Queue的方法之外,还支持insert, remove和examine操作,由于Deque是双向的,所以可以对队列的头和尾都进行操作。Deque既可以当做栈使用,也可以当做队列、双端队列使用。
    在这里插入图片描述

  • ArrayDequeLinkedListDeque的两个通用实现,官方更推荐使用AarryDeque用作栈和队列,本节总结ArrayDeque

二、ArrayDeque

ArrayDeque层次结构图

在这里插入图片描述

ArrayDeque概述

从名字可以看出ArrayDeque底层通过数组实现,为了满足可以同时在数组两端插入或删除元素的需求,该数组还必须是循环的,即循环数组(circular array),也就是说数组的任何一点都可能被看作起点或者终点。ArrayDeque是非线程安全的(not thread-safe),当多个线程同时使用的时候,需要程序员手动同步;另外,该容器不允许放入null元素
在这里插入图片描述

上图中我们看到,head指向首端第一个有效元素,tail指向尾端第一个可以插入元素的空位。因为是循环数组,所以head不一定总等于0,tail也不一定总是比head大。

ArrayDeque底层数据结构

循环数组

ArrayDeque常用方法(简略)

详细介绍参考博客 ArrayDeque详解

  • addFirst()

向头部添加元素

public void addFirst(E e) {
    //不允许元素为 null
    if (e == null)
        throw new NullPointerException();
    //使用头部参数计算下标
    elements[head = (head - 1) & (elements.length - 1)] = e;
    if (head == tail)
        //如果头部与尾部重合,进行数组扩容
        doubleCapacity();
}

  • addLast()

表示向尾部添加元素

public void addLast(E e) {
    //不允许放入null
    if (e == null)
        throw new NullPointerException();
    elements[tail] = e;//将元素插入到尾部
    //将尾部进行+1,判断下标是否越界
    if ( (tail = (tail + 1) & (elements.length - 1)) == head)
        //数组下标越界,进行扩容
        doubleCapacity();
}

  • pollFirst()

表示删除头部元素,并返回删除的元素

public E pollFirst() {
    //获取数组头部
    int h = head;
    E result = (E) elements[h];
    //判断头部元素是否为空
    if (result == null)
        return null;
    //设为null,方便GC回收
    elements[h] = null;
    //向上移动头部元素
    head = (h + 1) & (elements.length - 1);
    return result;
}

  • pollLast()

对数组尾部元素进行删除,并返回元素

public E pollLast() {
    //通过尾部计算数组下标
    int t = (tail - 1) & (elements.length - 1);
    E result = (E) elements[t];
    //判断是否为空
    if (result == null)
        return null;
    //设为null
    elements[t] = null;
    tail = t;
    return result;
}
  • peekFirst()

通过数组头部获取数组元素

public E peekFirst() {
    //可能返回null
    return (E) elements[head];
}

  • peekLast()

通过数组尾部获取数组元素

public E peekFirst() {
    //可能返回null
    return (E) elements[(tail - 1) & (elements.length - 1)];
}

三、PriorityQueue

PriorityQueue层次结构图

在这里插入图片描述

PriorityQueue概述

在这里插入图片描述

  • 前面以Java ArrayDeque为例讲解了Stack和Queue,其实还有一种特殊的队列叫做PriorityQueue,即优先队列。优先队列的作用是能保证每次取出的元素都是队列中权值最小的(Java的优先队列每次取最小元素,C++的优先队列每次取最大元素)。这里牵涉到了大小关系,元素大小的评判可以通过元素本身的自然顺序(natural ordering),也可以通过构造时传入的比较器(Comparator,类似于C++的仿函数)。
  • Java中PriorityQueue实现了Queue接口,不允许放入null元素;其通过堆实现,具体说是通过完全二叉树(complete binary tree)实现的小顶堆(任意一个非叶子节点的权值,都不大于其左右子节点的权值),也就意味着可以通过数组来作为PriorityQueue的底层实现。
  • 父节点和子节点的编号是有联系的,更确切的说父子节点的编号之间有如下关系:
    • leftNo = parentNo*2+1
    • rightNo = parentNo*2+2
    • parentNo = (nodeNo-1)/2
  • 通过上述三个公式,可以轻易计算出某个节点的父节点以及子节点的下标。这也就是为什么可以直接用数组来存储堆的原因。
  • PriorityQueue的peek()element操作是常数时间,add(), offer(), 无参数的remove()以及poll()方法的时间复杂度都是log(N)

PriorityQueue 底层数据结构

PriorityQueue的底层数据结构是堆,具体是完全二叉树(complete binary tree)实现的小顶堆

PriorityQueue常用方法(详细)

参考 priorityQueue方法详解

  • add()和offer()
    在这里插入图片描述

add(E e)和offer(E e)的语义相同,都是向优先队列中插入元素,只是Queue接口规定二者对插入失败时的处理不同,前者在插入失败时抛出异常,后则则会返回false。对于PriorityQueue这两个方法其实没什么差别。

新加入的元素可能会破坏小顶堆的性质,因此需要进行必要的调整

//offer(E e)
public boolean offer(E e) {
    if (e == null)//不允许放入null元素
        throw new NullPointerException();
    modCount++;
    int i = size;
    if (i >= queue.length)
        grow(i + 1);//自动扩容
    size = i + 1;
    if (i == 0)//队列原来为空,这是插入的第一个元素
        queue[0] = e;
    else
        siftUp(i, e);//调整
    return true;
}

上述代码中,扩容函数grow()类似于ArrayList里的grow()函数,就是再申请一个更大的数组,并将原数组的元素复制过去,这里不再赘述。需要注意的是siftUp(int k, E x)方法,该方法用于插入元素x并维持堆的特性。

//siftUp()
private void siftUp(int k, E x) {
    while (k > 0) {
        int parent = (k - 1) >>> 1;//parentNo = (nodeNo-1)/2
        Object e = queue[parent];
        if (comparator.compare(x, (E) e) >= 0)//调用比较器的比较方法
            break;
        queue[k] = e;
        k = parent;
    }
    queue[k] = x;
}

新加入的元素x可能会破坏小顶堆的性质,因此需要进行调整。调整的过程为: 从k指定的位置开始,将x逐层与当前点的parent进行比较并交换,直到满足x >= queue[parent]为止。注意这里的比较可以是元素的自然顺序,也可以是依靠比较器的顺序。

  • element()和peek()
    在这里插入图片描述

element()peek()的语义完全相同,都是获取但不删除队首元素,也就是队列中权值最小的那个元素,二者唯一的区别是当方法失败时前者抛出异常后者返回null。根据小顶堆的性质,堆顶那个元素就是全局最小的那个;由于堆用数组表示,根据下标关系,0下标处的那个元素既是堆顶元素。所以直接返回数组0下标处的那个元素即可。

//peek()
public E peek() {
    if (size == 0)
        return null;
    return (E) queue[0];//0下标处的那个元素就是最小的那个
}
  • remove()和poll()
    在这里插入图片描述

remove()和poll()方法的语义也完全相同,都是获取并删除队首元素,区别是当方法失败时前者抛出异常,后者返回null。由于删除操作会改变队列的结构,为维护小顶堆的性质,需要进行必要的调整。

public E poll() {
    if (size == 0)
        return null;
    int s = --size;
    modCount++;
    E result = (E) queue[0];//0下标处的那个元素就是最小的那个
    E x = (E) queue[s];
    queue[s] = null;
    if (s != 0)
        siftDown(0, x);//调整
    return result;
}

上述代码首先记录0下标处的元素,并用最后一个元素替换0下标位置的元素,之后调用siftDown()方法对堆进行调整,最后返回原来0下标处的那个元素(也就是最小的那个元素)。重点是siftDown(int k, E x)方法,该方法的作用是从k指定的位置开始,将x逐层向下与当前点的左右孩子中较小的那个交换,直到x小于或等于左右孩子中的任何一个为止

//siftDown()
private void siftDown(int k, E x) {
    int half = size >>> 1;
    while (k < half) {
        //首先找到左右孩子中较小的那个,记录到c里,并用child记录其下标
        int child = (k << 1) + 1;//leftNo = parentNo*2+1
        Object c = queue[child];
        int right = child + 1;
        if (right < size &&
            comparator.compare((E) c, (E) queue[right]) > 0)
            c = queue[child = right];
        if (comparator.compare(x, (E) c) <= 0)
            break;
        queue[k] = c;//然后用c取代原来的值
        k = child;
    }
    queue[k] = x;
}
  • remove(Object o)
    在这里插入图片描述

remove(Object o)方法用于删除队列中跟o相等的某一个元素(如果有多个相等,只删除一个),该方法不是Queue接口内的方法,而是Collection接口的方法。由于删除操作会改变队列结构,所以要进行调整;又由于删除元素的位置可能是任意的,所以调整过程比其它函数稍加繁琐。具体来说,remove(Object o)可以分为2种情况: 1. 删除的是最后一个元素。直接删除即可,不需要调整。2. 删除的不是最后一个元素,从删除点开始以最后一个元素为参照调用一次siftDown()即可。此处不再赘述。

//remove(Object o)
public boolean remove(Object o) {
    //通过遍历数组的方式找到第一个满足o.equals(queue[i])元素的下标
    int i = indexOf(o);
    if (i == -1)
        return false;
    int s = --size;
    if (s == i) //情况1
        queue[i] = null;
    else {
        E moved = (E) queue[s];
        queue[s] = null;
        siftDown(i, moved);//情况2
        ......
    }
    return true;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1631210.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt | 标准、复选、单选、工具、命令按钮大全

01、QPushButton QPushButton 类(标准按钮) 示例 3:默认按钮与自动默认按钮 02、QCheckBox QCheckBox 类(复选按钮) 1、复选按钮的第三状态(见右图 Qt5.10.1 的选中状态):是指除了选中 和未选中状态之外的第三种状态,这种状态用来指示“不变”,表 示用户既不选中也不取…

go语言实现简单认证样例

目录 1、代码实现样例 2、postman调用 1、代码实现样例 package mainimport ("net/http""strings""github.com/dgrijalva/jwt-go""github.com/gin-gonic/gin" )var (// 密钥&#xff0c;用于验证 JWT 令牌signingKey []byte("…

09 配置文件日志多线程

配置文件 配置文件 在企业开发过程中&#xff0c;我们习惯把一些需要灵活配置的数据放在一些文本文件中&#xff0c;而不是在Java代码写死我们把这种存放程序配置信息的文件&#xff0c;统称为配置文件 配置文件一般要求有明确的格式&#xff0c;以方便读写操作!!! Properti…

【已解决】VMware虚拟机中出现无法将“iso文件”当做CD-ROM映像进行连接:找不到该文件的错误

报错 在VMware上运行虚拟机时报错&#xff1a;VMware虚拟机中出现无法将“iso文件”当做CD-ROM映像进行连接&#xff1a;找不到该文件的错误 原因分析 找不到这个iso文件了&#xff0c;检查文件位置 解决方案 找到iso文件&#xff0c;然后在VM中编辑虚拟机中这个文件的位置…

双塔模型在召回和粗排的区别

答案参考&#xff1a;推荐系统中&#xff0c;双塔模型用于粗排和用于召回的区别有哪些? - 知乎 召回和粗排在不同阶段面临样本不一样&#xff0c;对双塔来说样本分布差异会使召回和粗排采取不一样的方式。召回打分空间是全部item空间&#xff0c;曝光只有很少一部分&#xff0…

IDEA上文件换行符、分隔符(Line Separator)LF,CR,CRLF错乱影响Git上传Github或Gitee代码

IDEA上文件换行符、分隔符(Line Separator)LF&#xff0c;CR&#xff0c;CRLF错乱影响Git上传Github或Gitee代码 指定目录 然后就可以上传了 OK 一定注意更改Line Separator的文件目录 如果是target目录下的文件,是不能修改为LF的,把target文件删除,再重载一次main文件,就…

安装JAVA和java IDEA并汉化过程

1.安装java: 打开java的下载链接&#xff1a; Java Downloads | Oracle 然后选择对应的版本下载即可&#xff0c;我这里是windows 所以下载这个 然后正常一步步安装即可。 2.配置java环境&#xff1a; 在桌面右键此电脑然后点击属性——高级系统设置——环境变量——然后…

揭秘工业大模型:从人工智能小白到技术先锋

工业大模型的五个基本问题 信息化时代&#xff0c;数字化转型成为企业提升营运效率、应对经营风险和提升核心竞争力的重要途径。在此过程中&#xff0c;数据作为一种客观存在的资源&#xff0c;所产生的价值日益凸显。党的十九届四中全会从国家治理体系和治理能力现代化的高度将…

【启扬方案】启扬RK3568核心板助力酒店智能化入住与服务新体验!

近年来&#xff0c;随着人们对于便利和效率的需求不断提升&#xff0c;酒店行业也开始追求智能化的管理方式。酒店无人自助机系统整合了人机交互、人脸识别、智能化控制、语音识别等技术&#xff0c;结合软件应用&#xff0c;房客可以自助完成身份识别、选房、支付、自动发放房…

Hot100-哈希法

1. 两数之和 - 力扣&#xff08;LeetCode&#xff09; 在做面试题目的时候遇到需要判断一个元素是否出现过的场景应该第一时间想到哈希法 class Solution {public int[] twoSum(int[] nums, int target) {int[] result new int[2];for (int i 0; i < nums.length;i){for…

风险防不胜防?看YashanDB如何守护你的数据库安全(下篇)

前言 上一篇文章&#xff0c;咱们主要讲了数据库安全机制中的访问管理~今天继续深入聊聊威胁监测以及数据保护&#x1f9d0; &#xff08;文末可回顾上篇文章噢&#x1f4d6;&#xff09; 02 威胁监测 安全审计 YashanDB语法上支持权限审计、行为审计和角色审计&#xff0c;…

开发规范:API安全

开发规范&#xff1a;API安全 API是现代移动、SaaS和web应用程序的关键组成部分&#xff0c;可以应用在面向客户、合作伙伴和内部应用程序中。API可以暴露应用程序逻辑和敏感数据。不安全的API很容易成为黑客攻击的目标&#xff0c;使他们能够访问安全的服务器或网络。攻击者可…

甘特图是什么?利用甘特图来优化项目管理流程

在现代项目管理中,图表是一种强大而直观的工具,可以帮助项目经理和团队成员清晰地了解并掌控整个项目进程。其中,甘特图是最常用和最有效的图表之一。 甘特图是一种条形图,可以用来直观地展示项目中各个任务的进度、持续时间和相互关系。它由一个横轴和一个纵轴组成。横轴代表时…

Docker搭建LNMP+Wordpress的实验

目录 一、项目的介绍 1、项目需求 2、服务器环境 3、任务需求 二、Linux系统基础镜像 三、部署Nginx 1、建立工作目录 2、编写Dockerfile 3、准备nginx.conf配置文件 4、设置自定义网段和创建镜像和容器 5、启动镜像容器 6、验证nginx 三、Mysql 1、建立工作目录…

Vue集成three.js,加载glb、gltf类型的3d模型

安装基本依赖 // 注意OrbitControls要加{}&#xff0c;注意路径是jsm import { OrbitControls } from ‘three/examples/jsm/controls/OrbitControls.js’; // import { dat } from ‘three/examples/jsm/controls/dat.gui.js’; // dat gui这个插件&#xff0c;是另外自己下载…

CentOS命令大全:掌握关键命令及其精妙用法!

CentOS是一种流行的开源企业级Linux发行版&#xff0c;它基于Red Hat Enterprise Linux (RHEL)的源代码构建。对于系统管理员和运维工程师来说&#xff0c;掌握CentOS的常用命令至关重要。 这些命令不仅可以帮助管理服务器&#xff0c;还可以进行故障排查、性能监控和安全加固等…

面试官:素有Java锁王称号的‘StampedLock’你知道吗?我:这什么鬼?

一、写在开头 我们在上一篇写ReentrantReadWriteLock读写锁的末尾留了一个小坑&#xff0c;那就是读写锁因为写锁的悲观性&#xff0c;会导致 “写饥饿”&#xff0c;这样一来会大大的降低读写效率&#xff0c;而今天我们就来将此坑填之&#xff01;填坑工具为&#xff1a;Stam…

牛客NC99 多叉树的直径【较难 深度优先 Java/Go/PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/a77b4f3d84bf4a7891519ffee9376df3 思路 核心就是树的最大直径(globalMax)一定是以某一个node为root最长的两个path-to-leaf. 就是普通dfs的同时算路径长度。时间: O(n), DFS一次 空间: O(n)参考答案Java impo…

制作一个RISC-V的操作系统十四-任务同步和锁

文章目录 并发与同步临界区和锁锁死锁解决死锁自旋锁&#xff08;spin lock&#xff09;原子性问题原子操作实现amoswap.w.aq例子 另一种方法自旋锁的注意事项代码其他同步技术 并发与同步 控制流&#xff1a;可理解为任务或进程 中断也可以理解为一个切换到另一个任务&#…

Webpack-入门

定义 静态模块&#xff1a;指的是编写代码过程中的html&#xff0c;css&#xff0c;js&#xff0c;图片等固定内容的文件 打包&#xff1a;把静态模块内容压缩、整合、翻译等&#xff08;前端工程化&#xff09; 1&#xff09;把less/sass转成css代码 2&#xff09;把ES6降级…