集合Queue、Deque、LinkedList、ArrayDeque、PriorityQueue详解

1、 Queue与Deque的区别

在研究java集合源码的时候,发现了一个很少用但是很有趣的点:Queue以及Deque;
平常在写leetcode经常用LinkedList向上转型Deque作为栈或者队列使用,但是一直都不知道Queue的作用,于是就直接官方文档好了。
Queue和Deque:
Deque是Queue的子接口;
从源码中可以得知:Queue以及Deque都是继承于Collection,Deque是Queue的子接口。
public interface Deque<E> extends Queue<E> {}

Queue——单端队列;Deque——双端队列;
从Deque的解释中,我们可以得知:Deque是double ended queue,我将其理解成双端队列,就是可以在首和尾都进行插入或删除元素。
而Queue的解释中,Queue就是简单的FIFO(先进先出)队列。所以在概念上来说,Queue是FIFO的单端队列,Deque是双端队列。

Queue常用子类——PriorityQueue;Deque常用子类——LinkedList以及ArrayDeque;
Queue有一个直接子类PriorityQueue。
而Deque中直接子类有两个:LinkedList以及ArrayDeque。

 PriorityQueue:
我觉得重点就在圈定的两个单词:无边界的,优先级的堆。
从源码中,明显看到PriorityQueue的底层数据结构是数组,而无边界的形容,那么指明了PriorityQueue是自带扩容机制的,具体请看PriorityQueue的grow方法。 

2 PriorityQueue源码解读

top k算法的经典实现是大顶堆和小顶堆,而在JAVA中可以用PriorityQueue实现小顶堆,话不多说,直接上代码

public static List<Integer> getTopMapNum(int[] arr, int k) {Queue<Integer> priorityQueue = new PriorityQueue();List<Integer> topKList = new ArrayList<>();if (arr == null || k > arr.length || k <= 0) {return topKList;}for (int i : arr) {if (priorityQueue.size() < k) {priorityQueue.add(i);} else {if (priorityQueue.peek() < i) {priorityQueue.poll();priorityQueue.add(i);}}}while (k-- > 0) {topKList.add(priorityQueue.poll());}return topKList;
}

作为程序员,只知道简单的如何实现是不够的,最好能够深入源码,下面我们就来聊一聊PriorityQueue
PriorityQueue是优先队列,作用是保证每次取出的元素都是队列中权值最小的,这里涉及到了大小关系,元素大小的评判可以通过元素自身的自然顺序(使用默认的比较器),也可以通过构造时传入的比较器。

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)。

 

方法解析(JDK 1.8)
add()和offer
add()方法内部是调用的offer(),所以两个方法没啥区别,都是向队列中插入元素

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

/*** The number of times this priority queue has been* <i>structurally modified</i>.  See AbstractList for gory details.*队列调整的次数*/
transient int modCount = 0; // non-private to simplify nested class access
/*** The number of elements in the priority queue.*队列中元素的个数*/
private int size = 0;
public boolean add(E e) {//add方法内部调用的offer方法return offer(e);
}
public boolean offer(E e) {if (e == 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;
}
/*** Increases the capacity of the array.*队列扩容* @param minCapacity the desired minimum capacity*/
private void grow(int minCapacity) {//原始队列容量int oldCapacity = queue.length;// Double size if small; else grow by 50%//如果原始队列容量没有超过64,则翻倍扩容,否则扩容50%int newCapacity = oldCapacity + ((oldCapacity < 64) ?(oldCapacity + 2) :(oldCapacity >> 1));// overflow-conscious code//如果扩容后的队列大小超过了最大队列大小,则需要进行特殊处理if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);queue = Arrays.copyOf(queue, newCapacity);
}
private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}
/*** Inserts item x at position k, maintaining heap invariant by* promoting x up the tree until it is greater than or equal to* its parent, or is the root.** To simplify and speed up coercions and comparisons. the* Comparable and Comparator versions are separated into different* methods that are otherwise identical. (Similarly for siftDown.)** @param k the position to fill* @param x the item to insert* 元素添加*/
private void siftUp(int k, E x) {//使用构造方法传进来的比较器if (comparator != null)siftUpUsingComparator(k, x);elsesiftUpComparable(k, x);//使用默认的比较器
}
private void siftUpComparable(int k, E x) {Comparable<? super E> key = (Comparable<? super E>) x;while (k > 0) {//获取父节点的下标int parent = (k - 1) >>> 1;//父节点的元素值Object e = queue[parent];//如果新插入的元素比父节点的元素值大,循环结束,新插入节点直接插入最后即可if (key.compareTo((E) e) >= 0)break;//否则需要把父节点元素值放到新插入节点的下标(可以理解为父节点与新插入元素调换位置)queue[k] = e;//重复进行,知道父节点比子节点小k = parent;}//新插入元素放入排序后的下标queue[k] = key;
}

poll 类似,poll 从上往下进行,然后将左右进行比较,将小的一个和 父节点交换

3 LinkedList以及ArrayDeque:

从官方解释来看,ArrayDeque是无初始容量的双端队列,LinkedList则是双向链表。
而我们还能看到,ArrayDeque作为队列时的效率比LinkedList要高。
而在栈的使用场景下,无疑具有尾结点,不需判空的LinkedList更为高效。
演示ArrayDeque作为队列以及LinkedList作为栈的使用:

private static void usingAsQueue() {Deque<Integer> queue=new ArrayDeque<>();System.out.println("队列为空:"+queue.isEmpty());   //判断队列是否为空queue.addLast(12);   //添加元素System.out.println(queue.peekFirst());   //获取队列首部元素System.out.println(queue.pollFirst());   //获取并移除栈顶元素}private static void usingAsStack() {//作为栈使用Deque<Integer> stack=new LinkedList<>();System.out.println("栈为空:"+stack.isEmpty());   //判断栈是否为空stack.addFirst(12);//添加元素System.out.println(stack.peekFirst());   //获取栈顶元素System.out.println(stack.pollFirst());   //获取并移除栈顶元素}

小提示: 
在Deque中,获取并移除元素的方法有两个,分别是removeXxx以及peekXxx。
存在元素时,两者的处理都是一样的。
但是当Deque内为空时,removeXxx会直接抛出NoSuchElementException,
而peekXxx则会返回null。
所以无论在实际开发或者算法时,推荐使用peekXxx方法。
其实ArrayDeque和LinkedList都可以作为栈以及队列使用,但是从执行效率来说,ArrayDeque作为队列,以及LinkedList作为栈使用,会是更好的选择。

注意:
ArrayDeque 是 Deque 接口的一种具体实现,是依赖于可变数组来实现的。ArrayDeque 没有容量限制,可根据需求自动进行扩容。ArrayDeque 可以作为栈来使用,效率要高于 Stack。ArrayDeque 也可以作为队列来使用,效率相较于基于双向链表的 LinkedList 也要更好一些。注意,ArrayDeque 不支持为 null 的元素。

之后,再说为什么建议使用ArrayDeque而不是LinkedList:
链表比数组花费更多空间
链表的随机访问性质比数组差(虽然这个对栈来说问题不大)
链表的每次插入和删除都涉及到一个节点对象的创建和弃用,非常低效和浪费空间,而动态数组几乎是0花费的(数组充满时重新拷贝除外)
链表是非连续的,访问时候不能充分利用cpu cache

 

所以无论是栈还是队列,JDK都是建议使用ArrayDeque而不是LinkedList实现,ArrayDeque比较复杂的一点就是需要指定初始大小,当然你不指定也行。但是它的效率确实是比LinkedList高的

小结:
PriorityQueue可以作为堆使用,而且可以根据传入的Comparator实现大小的调整,会是一个很好的选择。
ArrayDeque可以作为栈或队列使用,但是栈的效率不如LinkedList高,通常作为队列使用。
LinkedList可以作为栈或队列使用,但是队列的效率不如ArrayQueue高,通常作为栈使用。

Queue和Deque的方法区别:
在java中,Queue被定义成单端队列使用,Deque被定义成双端队列使用。
而由于双端队列的定义,Deque可以作为栈或者队列使用;
而Queue只能作为队列或者依赖于子类的实现作为堆使用。
方法上的区别如下:

add  offer  add 底层调用offer 无区别
remove 无元素 抛出异常  poll 返回null    删除元素
peek  element 不删除元素 ,element 无元素 抛出异常,peek 返回null

LinkedList与ArrayDeque在栈与队列中的使用

LinkedList

增加:
add(E e):在链表后添加一个元素; 通用方法 
addLast(E e):在链表尾部添加一个元素; 特有方法
offer(E e):在链表尾部插入一个元素
offerLast(E e):JDK1.6本之后,在尾部添加; 特有方法

特点:add 与 addLast  一个有返回值 boolean,一个无
offer 与 add  与 offerLast 一样

addFirst(E e):在链表头部插入一个元素; 特有方法
offerFirst(E e):JDK1.6版本之后,在头部添加; 特有方法

特点:offerFirst   有返回值  addFirst无
add(int index, E element):在指定位置插入一个元素。

删除:
remove() :移除链表中第一个元素; 通用方法
removeFirst() 移除第一个元素
pollFirst():删除头; 特有方法
pop():和removeFirst方法一致,删除头。 ==
poll():查询并移除第一个元素 特有方法 ==

特点:remove 抛异常,poll 返回null    pop  与 removeFirst  remove一致
remove(E e):移除指定元素; 通用方法
removeFirst(E e):删除头,获取元素并删除; 特有方法
removeLast(E e):删除尾; 特有方法

pollLast():删除尾; 特有方法

查:
poll():查询并移除第一个元素 特有方法
pollFirst():查询并删除头; 特有方法
getFirst():获取第一个元素; 特有方法
peek():获取第一个元素,但是不移除; 特有方法
peekFirst():获取第一个元素,但是不移除;

特点:poll 弹出数据,无数据返回null  :getFirst 无数据 抛出异常,   peek 返回数据,无数据返回null
getLast():获取最后一个元素; 特有方法
peekLast():获取最后一个元素,但是不移除;
pollLast():删除尾; 特有方法
get(int index):按照下标获取元素; 通用方法

ArrayDeque
特点:
ArrayDeque实现了Deque接口。可当作栈来用,效率高于stack。也可当作队列来用,效率高于LinkedList。
底层用可变数组实现,无容量限制。
ArrayDeque是不安全的。

增加:
addFirst(E e)在数组前面添加元素
addLast(E e)在数组后面添加元素
offerFirst(E e)在数组前面添加元素,并返回是否添加成功
offerLast(E e)在数组后添加元素,并返回是否添加成功
push(E e):与addFirst方法一致
offer(E e):在链表尾部插入一个元素

删除:
removeFirst()删除第一个元素,并返回删除元素的值,如果为null,将抛出异常
removeLast()删除最后一个元素,并返回删除元素的值,如果为null,将抛出异常
pollFirst()删除第一个元素,并返回删除元素的值,如果为null,将返回null
pollLast()删除最后一个元素,并返回删除元素的值,如果为null,将返回null

pop():和removeFirst方法一致,删除头。 ==
poll():查询并移除第一个元素 特有方法

查:
getFirst()获取第一个元素,如果为null,将抛出异常
getLast()获取最后一个元素,如果为null,将抛出异常
peek():获取第一个元素,但是不移除; 特有方法

注意
add() 和 offer()的区别
在容量已满的情况下,add() 方法会抛出IllegalStateException异常,offer() 方法只会返回 false
remove方法和poll方法都是删除队列的头元素,remove方法,队列为空的情况下将抛异常,而poll方法将返回null;
element和peek方法都是返回队列的头元素,但是不删除头元素,区别在与element方法在队列为空的情况下,将抛异常,而peek方法将返回null。


 

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

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

相关文章

Qwen2.5-7B大模型微调记录

Qwen2.5-7B大模型微调记录 研究需要&#xff0c;需要搞一个大模型出来&#xff0c;没有太多的时间自己训练&#xff0c;准备用现成的开源大模型&#xff0c;然后结合研究方向进行微调 前前后后折腾大半个月&#xff0c;总算做完了第一个微调的大模型&#xff0c;模型基于阿里…

Spring Boot 开发环境搭建及示例应用

文章目录 1. 准备开发工具安装 JDK安装 IDE安装 Maven 2. 创建 Spring Boot 项目使用 Spring Initializr 创建项目导入项目到 IDE 3. 编写代码主类创建控制器创建实体类创建仓库接口创建服务类创建控制器 4. 配置文件5. 运行应用程序6. 测试应用程序7. 调试与优化8. 部署应用 1…

CentOS Docker 安装

CentOS Docker 安装 1. 简介 CentOS 是一个基于 Red Hat Enterprise Linux (RHEL) 的开源操作系统&#xff0c;广泛用于服务器环境。Docker 是一个开源的应用容器引擎&#xff0c;它允许开发者打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的…

docker 的各种操作

Docker pull拉取镜像报错“Error response from daemon: Get "https://registry-1.docker.io/v2”解决办法&#xff1a; 解决方法&#xff1a;将 /etc/docker/daemon.json 中的"registry-mirrors"的内容换成如下内容 { "registry-mirrors": [ "…

在xml的sql的子查询中使用row_number over之后再在mapper的接口层传入Page对象实现分页功能,出现Bug

1.报错信息复现&#xff1a; Mapper接口&#xff1a; List<UserInfo> queryUserPage(Param(“vo”) UserQury query,Page<UserInfo> page); UserQury 类中的状态字段&#xff1a; ApiModelproperty(“状态”) private String status; Xml中sql如下&#xff1…

动态规划之背包问题

0/1背包问题 1.二维数组解法 题目描述&#xff1a;有一个容量为m的背包&#xff0c;还有n个物品&#xff0c;他们的重量分别为w1、w2、w3.....wn&#xff0c;他们的价值分别为v1、v2、v3......vn。每个物品只能使用一次&#xff0c;求可以放进背包物品的最大价值。 输入样例…

蓝桥杯模拟题不知名题目

题目:p是一个质数&#xff0c;但p是n的约数。将p称为是n的质因数。求2024最大质因数。 #include<iostream> #include<algorithm> using namespace std; bool fun(int x) {for(int i 2 ; i * i < x ; i){if(x % i 0)return false;}return true; } int main() …

JavaFX 表格组件详解及案例

1. 表格组件简介 TableView&#xff1a;用于显示数据的二维表格&#xff0c;支持列排序、选择、编辑等功能。TreeTableView&#xff1a;类似于 TableView&#xff0c;但支持分层数据展示&#xff0c;适合树形结构的数据。 2. TableView 的常用方法 2.1 构造方法 TableView()…

cocoscreater3.8.4生成图集并使用

1.安装texturepacker&#xff0c;去官网下载https://www.codeandweb.com/texturepacker 2.将图片拖动进来&#xff0c;即可自动生成精灵表&#xff0c;这里输出选用cocos2d-x&#xff0c;打包用免费版的“基本”就行&#xff0c;高级模式是收费的&#xff0c;然后点击“发布精…

解决SSL VPN客户端一直提示无法连接服务器的问题

近期服务器更新VPN后&#xff0c;我的win10电脑一致无法连接到VPN服务器&#xff0c; SSL VPN客户端总是提示无法连接到服务端。网上百度尝试了各种方法后&#xff0c;终于通过以下设置方式解决了问题&#xff1a; 1、首先&#xff0c;在控制面板中打开“网络和共享中心”窗口&…

Docker login 报证书存储错误的解决办法

文章目录 docker login 出现错误&#xff0c;提示&#xff1a;Error saving credentials: error storing credentials - err: exit status 1, out: Cannot autolaunch D-Bus without X11 $DISPLAY 环境 使用的是 Mint Linux &#xff0c;容器为 docker-ce 最新版 1 2 3 4 $…

从零开始学GeoServer源码(二)添加支持arcgis切片功能

文章目录 参考文章环境背景1、配置打包好的程序1.1、下载GeoServer的war包1.2、下载GeoWebCache1.3、拷贝jar包1.4、修改配置文件1.4.1、拷贝geowebcache-arcgiscache-context.xml1.4.2、修改geowebcache-core-context.xml1.4.3、修改geowebcache-servlet.xml 1.5、配置切片信息…

【Docker】Centos7 Jenkins 踩坑笔记

文章目录 1. docker pull 超时2. 初始化找不到 initialAdminPassword 1. docker pull 超时 docker pull 命令拉不下来 docker pull jenkins/jenkins:lts-jdk17 Error response from daemon: Get "https://registry-1.docker.io/v2/": 编辑docker配置 sudo mkdir -…

Java中的JSONObject详解

文章目录 Java中的JSONObject详解一、引言二、JSONObject的创建与基本操作1、创建JSONObject2、添加键值对3、获取值 三、JSONObject的高级特性1、遍历JSONObject2、从字符串创建JSONObject3、JSONObject与JSONArray的结合使用4、更新和删除键值对 四、错误处理1. 键值存在性检…

【大数据学习 | Spark-Core】Spark中的join原理

join是两个结果集之间的链接&#xff0c;需要进行数据的匹配。 演示一下join是否存在shuffle。 1. 如果两个rdd没有分区器&#xff0c;分区个数一致 &#xff0c;会发生shuffle。但分区数量不变。 scala> val arr Array(("zhangsan",300),("lisi",…

springMVC 全局异常统一处理

全局异常处理⽅式⼀: 1、配置简单异常处理器 配置 SimpleMappingExceptionResolver 对象: <!-- 配置全局异常统⼀处理的 Bean &#xff08;简单异常处理器&#xff09; --> <bean class"org.springframework.web.servlet.handler.SimpleMappingExceptionReso…

111.有效单词

class Solution {public boolean isValid(String word) {if(word.length()<3){return false;}int countV0,countC0;//分别统计原音和辅音for(int i0;i<word.length();i){if(Character.isLetterOrDigit(word.charAt(i))){if(word.charAt(i)a||word.charAt(i)e||word.charA…

安装python拓展库pyquery相关问题

我采用的是离线whl文件安装, 从官方库地址: https://pypi.org/, 下载whl文件, 然后在本地电脑上执行pip install whl路径文件名.whl 但是在运行时报错如下图 大体看了看, 先是说了说找到了合适的 lxml>2.1, 在我的python库路径中, 然后我去看了看我的lxml版本, 是4.8.0, 对…

如何启动多个libvirtd进程

导语:如何启动多个libvirtd,咋一想这不简单,多运行几个libvirtd不就完事,其实不然?为什么启动多个libvirtd,有何应用场景?当前libvirt代码架构是否支持启动多个libvirtd? 一、如何启动libvirtd # /usr/local/sbin/libvirtd --listen --listen 必须参数,监控tcp/ip c…

Unity中的简易TCP服务器/客户端

在本文中&#xff0c;我将向你介绍一个在Unity中实现的简单TCP服务器脚本,和一个简单的客户端脚本. 脚本 MyTcpServer 允许Unity应用创建一个TCP服务器&#xff0c;监听客户端的连接、异步处理客户端消息&#xff0c;并通过事件与Unity应用中的其他模块进行通信。 MyTcpServe…