范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

CollectionPriorityQueue源码解析,原来源码能这么简单?

  概述
  前面以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) 。方法剖析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并维持堆积的特性。private void siftUp(int k, E x) {     if (comparator != null)         siftUpUsingComparator(k, x);     else         siftUpComparable(k, x); }  @SuppressWarnings("unchecked") 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; }  @SuppressWarnings("unchecked") private void siftUpUsingComparator(int k, E x) {     while (k > 0) {         int parent = (k - 1) >>> 1;         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; }

老婆递给我的,差点当鱿鱼丝吃了,看了半天也不知道是啥夫妻之间适当的恶作剧可以提升感情,让彼此更好的信任,不过有些时候开玩笑也要注意场合和地方,在家里也就罢了如果在外面的话还是应该给老公多多少少的面子,你们有没有被另一半戏弄过的时候?银行卡强大的关联功能我的花样生活网络无所不在,真是太强大了,以前证券开户不仅要排队,而且还要收费现在网上新开了一个股票账户,是免费开户的。在疫情还没结束前确实是安全省事省时高效率。言归正题,开户需绑定寻找支付宝理财蚂蚁星愿计划入口我的花样生活其实好长一段时间一直使用支付宝。原因就是方便,用支付宝在超市消费可以选择信用卡,但是在菜市场普遍不支持信用卡支付,只能是支持花呗,现金,银行卡。但是这段时间,如果在超市老一辈人给年轻人挖的最大的坑是什么?哈哈原来有这么多啊?老一辈人给年轻人挖的最大的坑是什么?哈哈哈哈哈哈原来有这么多啊?虽然说老一辈人说的话都很有道理,但是他们给我们年轻人挖的坑也是不少。那么小伙伴们,你们觉得老一辈人给年轻人挖的最大的我哥给了我瓶这个,喝完以后怎么全身刺挠?啊哈哈哈哈我哥给了我瓶这个,很贵吗?喝完以后怎么全身刺挠?啊哈哈哈哈!!我哥给了我一瓶这个很贵吗?喝完以后全身刺挠。看到图中这篇就小编也是没有见过,但是看下面网友的评论说这是骷髅头氮泵健身用这东西到底是啥啊,割了又长好烦人啊!哈哈哈哈哈哈虽然说皮肤病并不是什么大病,一时间也不会很受罪,但有些皮肤病却反反复复,就是不能好的果断,让人也是很心烦的,又或者是其他一些皮肤上长的东西,即便是割了还会重新发芽,更是闹心,比如说朋友养了一只蛙当宠物,我打算笑话他,结果我错了!这蛙真帅有个出租车司机在火车站拉了一名乘客,见乘客老实巴交的样子,又是外地人,就想宰他一下,于是拉着他在火车站附近绕了一圈又一圈。绕到第六圈的时候,乘客指着路边的一尊雕像,对司机说你们这座伴娘都漂亮成这模样了,还给不给新娘面子的啊?哈哈哈哈哈大学毕业后,我已经工作三年了,我最近谈了一个女朋友。就在昨天,我第一次带女朋友回家了,我们一整天都相处得很愉快,我爸妈表现得都很喜欢这个女孩儿。下午时,我送女朋友回家后。老爸儿子,小米mix4的进一步消息就在前天,型号为2106118C的小米5G新机已获得3C认证,配备20V6A120W充电器,不出意外就是小米MIX4了,今天爆料达人还透露了些细节。据爆料达人暗示,小米MIX4将是中端新能源市场起风波?雷诺江铃羿正式上市,性能配置初曝光文岸青9月26日,雷诺江铃推出了旗下的首款产品雷诺江铃羿正式上市,此次新车一共推出了六款车型,售价14万元起,定位为紧凑型纯电动汽车,还提供了单电机与双电机两个版本,全方位满足需求2021年最后两个月快递驿站还能加入吗?收益怎么样?男怕入错行,女怕嫁错郎。其实现在很多女性朋友也怕入错行,现在很多90后00后也开始选择创业。快递驿站因其低门槛低投入高收益高稳定的优势成为很多人的首选,只要能吃苦就能获得稳定的收益
高速固态U盘不只有速度快,让aigo固态U盘U393给大家上一课随着工作重心转向视频剪辑,各种录制素材花字模板等等也占据了我电脑的大半空间。为了让电脑本身负担不会过重,我开始利用U盘进行各种内容的存储,也方便同事间阅览传输。在购入新U盘时,我才汪滔,大疆创始人,傲视群雄的无人机领导者汪滔,大疆创始人,1980年出生于浙江杭州。他的父亲是一名工程师,母亲是一名教师。小时候他就沉迷于航模,对天空充满幻想,因此他的主要精力都在飞行器上了,导致成绩并不突出,但汪滔依然小米推出米家扫拖机器人2,加入超声波震动拖,地下有渍都拖到小米官方正式宣布发布洁净力升级的米家扫拖机器人2。现在该机器人已开启了全渠道预售,原价1799元人民币,预售价仅需1599元。根据官方的介绍,米家扫拖机器人2进行了全方位的升级。最荣耀X30Max,双扬声超长续航,带来更好的沉浸式体验一转眼2021年仅剩俩月,而今年年手机行业中唯一的大屏5G手机赶在这黄金十月问世了。对于喜欢长续航玩手游及观影刷剧的朋友来说,大屏手机无疑能带来更好的沉浸式体验。现在就让我们一起来硬盘装满了?NAS重度患者的2个选择NAS重度用户经常在不知不觉中,就遇到NAS盘装满了的尴尬境地,碰到这个情况是感慨又糟心,感慨的是竟然已经存这么多这么多的宝贵资料,糟心的的是接下来该怎么弄,是全面更换硬盘以扩充存郭广昌,复星集团创始人,要做中国的巴菲特1967年,郭广昌出生在浙江东阳的农村,寒窗苦读多年,1985年考上了复旦大学哲学系。由于他在大学的表现优异,所以获得留校任教的宝贵机会,郭广昌也因此作了一段时间的大学老师。199做好这些准备,活下来的可能性很大上个星期单位组织了消防安全培训,讲师播放了许多楼房包括高楼猛烈燃烧的视频。有妈妈哀嚎着将孩子抛出窗户的,有在窗台受不了800多度高温烟雾,猛烈燃烧的火焰决绝的一个个从高楼一跃跳下惨百元酒中精品,浓香型白酒谷小酒万里宋境,众筹二周破千万在中国传统节日里,探亲访友总少不了要带些礼物,而白酒更是各大传统节日首选。在竞争越来越白热化的白酒市场上,一款名为万里宋境的新品杀出重围,成为目前最受欢迎的大众品牌之一。出品它的谷加速机?云游戏?电视盒子?威联通TS551NAS上手新体验本着爱折腾的心,我入手了威联通TS551NAS。威联通TS551是一款5盘位NAS,3个HDD硬盘槽,2个SSD硬盘槽。它采用X86架构,搭载了IntelCeleronJ3355双灵活的扩容机威联通新品TR004RAID磁盘阵列外接盒评测数据爆炸时代,人们对于存储的需求正在发生改变,无论是手机电脑还是个人存储设备,都在面临着存储空间不足的问题。在一次又一次的提升设备存储空间中,人们也在逐渐调整自己的存储方式,云端存半万兆是什么鬼?拯救你纠结症威联通TS963N新品NAS评测什么叫做半万兆NAS?自从万兆之风吹起来后,大家纷纷好奇它的超高传输速度,但是又被高昂的配置价格所吓退!如何才能有一个价格与性能折中的方案?威联通近日推出了半万兆NASTS963N