Java集合框架-Collection-queue

目录

    • 一、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) {//不允许元素为 nullif (e == null)throw new NullPointerException();//使用头部参数计算下标elements[head = (head - 1) & (elements.length - 1)] = e;if (head == tail)//如果头部与尾部重合,进行数组扩容doubleCapacity();
}
  • addLast()

表示向尾部添加元素

public void addLast(E e) {//不允许放入nullif (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;//设为nullelements[t] = null;tail = t;return result;
}
  • peekFirst()

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

public E peekFirst() {//可能返回nullreturn (E) elements[head];
}
  • peekLast()

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

public E peekFirst() {//可能返回nullreturn (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;elsesiftUp(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)/2Object 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+1Object 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) //情况1queue[i] = null;else {E moved = (E) queue[s];queue[s] = null;siftDown(i, moved);//情况2......}return true;
}

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

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

相关文章

【Linux】进程的控制①之进程创建与进程退出

一 、进程的创建 1、fork函数 fork函数功能&#xff1a;从已经存在的进程中创建一个新进程。新进程为子进程&#xff0c;原进程为父进程。 fork函数创建进程过后&#xff0c;父子进程代码和数据是共享的。在前面也讲过。 2.函数的返回值 如果进程创建成功&#xff0c;给父进…

Linux中的vi与vim:编辑器的王者之争与深度探索

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Linux &#xff1a;从菜鸟到飞鸟的逆袭》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、前言 1、Linux的起源与发展 2、vi与vim的历史与发展 …

【ARMv9 DSU-120 系列 6 -- Power management】

请阅读【Arm DynamIQ™ Shared Unit-120 专栏 】 文章目录 Power management In DSU-120Power DomainsPDTopPDClusterPDCore<cn>PDComplex<cpxn>PDSlice<slicen>PDL3RAM0 和 PDL3RAM1Cluster power mode在线模式(On)

flutter笔记-webrtc使用1:依赖本地包socket.io-client

文章目录 1. 示例工程2. yaml 修改3. 使用4. socketio 关于自定义服务器自定义签名的问题封装成async和await方式 本文开始介绍webrtc的使用&#xff0c;阅读本文的前提是假设你已经使用过webrtc&#xff0c;了解webrtc的交互机制&#xff0c;不了解的可以看之前的文章&#xf…

springboot实现同时批量新增和批量修改数据

在springboot项目中&#xff0c;实现同时将一批数据进行新增和修改 有时候我们会遇到&#xff0c;用户在前端界面提交一批数据&#xff0c;而这一批数据中&#xff0c;可能有新增的数据也可能存在修改的数据&#xff0c;为了方便同时执行新增和修改的方法&#xff0c;需要调用…

Python轻量级Web框架Flask(12)—— Flask类视图实现前后端分离

0、前言&#xff1a; 在学习类视图之前要了解前后端分离的概念&#xff0c;相对于之前的模板&#xff0c;前后端分离的模板会去除views文件&#xff0c;添加两个新python文件apis和urls&#xff0c;其中apis是用于传输数据和解析数据 的&#xff0c;urls是用于写模板路径的。 …

数据库介绍(Mysql安装)

前言 工程师再在存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 一、什么是数据库&#xff1f; 文件保存数据有以下几个缺点&#xff1a; 文件的安全性问题文件不利于数据查询和管理文件不利于存储海量数据文件在程序中控制不方便 数据库存储介质&#xff1a; 磁…

【UE C++】设置游戏模式

问题 我们都知道如何使用蓝图创建一个游戏模式并且在这个游戏模式蓝图中去设置“默认pawn类”、“HUD类”、“玩家控制器类”、“游戏状态类”、“玩家状态类”、“旁观者类”。那么如何使用C完成该操作呢&#xff1f; 步骤 1. 首先创建“GameMode”、“GameState”、“HUD”…

Arthas:阿里出品,线上问题快速搞定!

前面我们通过JVM线程分析及内存分析来让大家从服务器资源异常情况下排查代码问题&#xff0c;类似这种的解决方式&#xff0c;更多的是在服务器资源占用已经异常显现&#xff0c;我们就可以按照这种方式去排查和解决。 但实际工作中&#xff0c;可能会出现&#xff1a;接口的TP…

惯导中基本的目标参数及其过程量

目标参数过程量(过程噪声&#xff09;目标参数物理含义常用单位过程量物理含义过程量物理对象单位功率谱密度单位(psd)rand walk error过程量目标参数/tunitpsd(unit)^2/hzsqrt(psd)常用单位单位换算速度(m/s)加速度m/s^2m^2/s^3m/s/sqrt(s)m/s/sqrt(hr)ug/sqrt(HZ)角度(rad)角…

boost::asio::io_context, boost::asio::steady_timer

asio::io_context是Asio库中的一个I/O执行环境&#xff0c;而asio::steady_timer是一个与asio::io_context关联的定时器&#xff0c;可以用来在给定的时间点执行一个函数或者是在一个固定的时间间隔后执行一个函数。 以下是一个创建asio::steady_timer的例子 #include <ios…

Paddle OCR v4 微调训练文字识别SVTRNet模型实践

文字识别步骤参考&#xff1a;https://github.com/PaddlePaddle/PaddleOCR/blob/main/doc/doc_ch/recognition.md 微调步骤参考:https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.7.1/doc/doc_ch/finetune.md 训练必要性 原始模型标点符号和括号容易识别不到 数据…

自动化机器学习:让机器学习更智能

自动化机器学习&#xff1a;让机器学习更智能 在当今数据驱动的时代&#xff0c;机器学习技术已经成为了许多行业和领域的核心。然而&#xff0c;随着数据量的增加和模型复杂度的提升&#xff0c;传统的机器学习方法往往需要大量的人力和时间进行调参和优化&#xff0c;这在某…

3步教你成为微信客户管理高手,助你事半功倍!

在如今的商业世界中&#xff0c;与客户建立良好的关系并提供个性化的服务已成为企业成功的关键。今天就 分享三个简单的步骤&#xff0c;让大家成为微信客户管理的高手&#xff0c;事半功倍&#xff01; 第一步&#xff1a;客户分类与精细化服务 为了更好地管理客户&#xff…

ROS2 ZEDX UBUNTU 环境配置

一、环境要求 硬件&#xff1a;镭神域控 ZED X双目摄像机 ros2-foxy zed sdk 4.0.8 cuda 11.4 二、CUDAZED SDK安装 安装显卡驱动以及CUDA 11.1 参考之前博文&#xff1a; https://blog.csdn.net/qq_41950533/article/details/135673420?csdn_share_tail%7B%22type%22%3A%2…

mongodb使用debezium

前置 服务器上需要安装jdk11 jdk下载地址 kafka安装 官网下载地址 安装教程 debezium 安装 运行 Debezium 连接器需要 Java 11 或更高版本 Debezium 并不是一个独立的软件&#xff0c;而是很多个 Kafka 连接器的总称。这些 Kafka 连接器分别对应不同的数据库&#xff0c;…

【C++】C\C++内存管理

下面是围绕C\C内存管理这一块知识谈论相关的内存管理机制&#xff0c;有需要借鉴即可。 同时&#xff0c;我在下面也放了快速建立链表的模板&#xff0c;方便oj题目拿到vs上进行调试。 内存管理目录 1.CPP内存管理1.1new、delete关键字概念1.2特性1.3总结 2.new、delete的底层…

电商API数据采集接口||大数据的发展,带动电子商务产业链,促进了社会的进步

最近几年计算机技术在诸多领域得到了有效的应用&#xff0c;同时在多方面深刻影响着我国经济水平的发展。除此之外&#xff0c;人民群众的日常生活水平也受大数据技术的影响。 主流电商API数据采集接口||在这其中电子商务领域也在大数据技术的支持下&#xff0c;得到了明显的进…

《逃离塔科夫》PVE模式加入付费特别版引发玩家不满

《逃离塔科夫》PVE模式加入付费特别版引发玩家不满 近期&#xff0c;《逃离塔科夫》开发者Battlestate发布了多个新版本&#xff0c;但其中PVE模式只能在价格最高的“Unheard Edition”中购买&#xff0c;导致玩家不满。据悉&#xff0c;“Unheard Edition”售价高达250欧元&a…

Liunx进程与端口

目录 查看进程 进程状态 &#xff08;了解&#xff09; TTY &#xff08;拓展&#xff09; 查看进程PID 1. pgrep 2. pidof 动态查看查看进程 top与htop的操作&#xff1a; 计算cpu负载&#xff1a; 进程控制 kill与pkill 查看端口 1. netstat 2. ss 3. lsof 查看…