java PriorityQueue

来源:互联网 发布:js 数组包含对象 编辑:程序博客网 时间:2024/05/22 10:45

优先级队列是一种队列结构,是0个或多个元素的集合,每个元素都有一个优先权,PriorityQueue被内置于JDK中,本文就来解析Java中PriorityQueue优先级队列结构的源码及用法.
一、PriorityQueue的数据结构
JDK7中PriorityQueue(优先级队列)的数据结构是二叉堆。准确的说是一个最小堆。
二叉堆是一个特殊的堆, 它近似完全二叉树。二叉堆满足特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆。
当父节点的键值总是大于或等于任何一个子节点的键值时为最大堆。 当父节点的键值总是小于或等于任何一个子节点的键值时为最小堆。
下图是一个最大堆
这里写图片描述
priorityQueue队头就是给定顺序的最小元素。
priorityQueue不允许空值且不支持non-comparable的对象。priorityQueue要求使用Comparable和Comparator接口给对象排序,并且在排序时会按照优先级处理其中的元素。
priorityQueue的大小是无限制的(unbounded), 但在创建时可以指定初始大小。当增加队列元素时,队列会自动扩容。
priorityQueue不是线程安全的, 类似的PriorityBlockingQueue是线程安全的。
我们知道队列是遵循先进先出(First-In-First-Out)模式的,但有些时候需要在队列中基于优先级处理对象。举个例子,比方说我们有一个每日交易时段生成股票报告的应用程序,需要处理大量数据并且花费很多处理时间。客户向这个应用程序发送请求时,实际上就进入了队列。我们需要首先处理优先客户再处理普通用户。在这种情况下,Java的PriorityQueue(优先队列)会很有帮助。
PriorityQueue是基于优先堆的一个无界队列,这个优先队列中的元素可以默认自然排序或者通过提供的Comparator(比较器)在队列实例化的时排序。
优先队列不允许空值,而且不支持non-comparable(不可比较)的对象,比如用户自定义的类。优先队列要求使用Java Comparable和Comparator接口给对象排序,并且在排序时会按照优先级处理其中的元素。
优先队列的头是基于自然排序或者Comparator排序的最小元素。如果有多个对象拥有同样的排序,那么就可能随机地取其中任意一个。当我们获取队列时,返回队列的头对象。
优先队列的大小是不受限制的,但在创建时可以指定初始大小。当我们向优先队列增加元素的时候,队列大小会自动增加。
PriorityQueue是非线程安全的,所以Java提供了PriorityBlockingQueue(实现BlockingQueue接口)用于Java多线程环境。
二、PriorityQueue源码分析
成员:

priavte transient Object[] queue;private int size = 0;

1.PriorityQueue构造小顶堆的过程
这里我们以priorityQueue构造器传入一个容器为参数PriorityQueue(Collecntion

private void initElementsFromCollection(Collection<? extends E> c) {  Object[] a = c.toArray();  // If c.toArray incorrectly doesn't return Object[], copy it.  if (a.getClass() != Object[].class)    a = Arrays.copyOf(a, a.length, Object[].class);  int len = a.length;  if (len == 1 || this.comparator != null)    for (int i = 0; i < len; i++)      if (a[i] == null)        throw new NullPointerException();  this.queue = a;  this.size = a.length;}

调整,使数据满足小顶堆的结构。
首先介绍两个调整方式siftUp和siftDown
siftDown: 在给定初始化元素的时候,要调整元素,使其满足最小堆的结构性质。因此不停地从上到下将元素x的键值与孩子比较并做交换,直到找到元素x的键值小于等于孩子的键值(即保证它比其左右结点值小),或者是下降到叶子节点为止。
例如如下的示意图,调整9这个节点:
这里写图片描述

private void siftDownComparable(int k, E x) {  Comparable<? super E> key = (Comparable<? super E>)x;  int half = size >>> 1;    // size/2是第一个叶子结点的下标  //只要没到叶子节点  while (k < half) {    int child = (k << 1) + 1; // 左孩子    Object c = queue[child];    int right = child + 1;    //找出左右孩子中小的那个    if (right < size &&      ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)      c = queue[child = right];    if (key.compareTo((E) c) <= 0)      break;    queue[k] = c;    k = child;  }  queue[k] = key;}

siftUp: priorityQueue每次新增加一个元素的时候是将新元素插入对尾的。因此,应该与siftDown有同样的调整过程,只不过是从下(叶子)往上调整。
例如如下的示意图,填加key为3的节点:
这里写图片描述

private void siftUpComparable(int k, E x) {  Comparable<? super E> key = (Comparable<? super E>) x;  while (k > 0) {    int parent = (k - 1) >>> 1;   //获取parent下标    Object e = queue[parent];    if (key.compareTo((E) e) >= 0)      break;    queue[k] = e;    k = parent;  }  queue[k] = key;}

总体的建立小顶堆的过程就是:

private void initFromCollection(Collection<? extends E> c) {    initElementsFromCollection(c);    heapify();  }

其中heapify就是siftDown的过程。
2.PriorityQueue容量扩容过程
从实例成员可以看出,PriorityQueue维护了一个Object[], 因此它的扩容方式跟顺序表ArrayList相差不多。
这里只给出grow方法的源码

private void grow(int minCapacity) {    int oldCapacity = queue.length;    // Double size if small; else grow by 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);  }

可以看出,当数组的Capacity不大的时候,每次扩容也不大。当数组容量大于64的时候,每次扩容double。
三、PriorityQueue的应用

import java.util.Comparator;import java.util.PriorityQueue;import java.util.Queue;public class test {    private String name;    private int population;    public test(String name, int population)    {        this.name = name;        this.population = population;    }    public String getName()    {         return this.name;    }    public int getPopulation()    {         return this.population;    }    public String toString()    {         return getName() + " - " + getPopulation();    }    public static void main(String args[])    {        Comparator<test> OrderIsdn =  new Comparator<test>(){            public int compare(test o1, test o2) {                // TODO Auto-generated method stub                int numbera = o1.getPopulation();                int numberb = o2.getPopulation();                if(numberb > numbera)                {                    return 1;                }                else if(numberb<numbera)                {                    return -1;                }                else                {                    return 0;                }            }        };        Queue<test> priorityQueue =  new PriorityQueue<test>(11,OrderIsdn);        test t1 = new test("t1",1);        test t3 = new test("t3",3);        test t2 = new test("t2",2);        test t4 = new test("t4",0);        priorityQueue.add(t1);        priorityQueue.add(t3);        priorityQueue.add(t2);        priorityQueue.add(t4);        System.out.println(priorityQueue.poll().toString());    }}

文章来自脚本之家、CSDN

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 手机卡到动不了怎么办 三星手机卡住了怎么办 手机突然动不了怎么办 苹果手机密码锁死了怎么办 节目指南无响应怎么办 电信卡网速不好怎么办 网上有人黑我怎么办? 公司前台没事做怎么办 股票跌到谷底了怎么办 cad无法进一步缩小怎么办 dnf更新错误280怎么办 win7电脑屏幕倒过来了怎么办 电脑画面缩小了怎么办 每次客户不付款怎么办 如果客户不付款怎么办 支付宝逾期起诉怎么办 卖号被骗了怎么办 网上交易视频被骗了怎么办 闲鱼卖游戏账号被骗怎么办 网银锁住了怎么办 电脑密码锁住了怎么办 苹果x旋转不了怎么办 苹果x黑白屏怎么办 dell网卡坏了怎么办 笔记本连网速慢怎么办 无线充电太慢怎么办 vivo手机速度慢怎么办 台式电脑很卡怎么办 电脑网卡驱动程序不正常怎么办 电脑没有网卡驱动怎么办 电脑网络网速差怎么办 小米wifi测速慢怎么办 电脑网速特别慢怎么办 戴尔电脑网速慢怎么办 电脑网络特别慢怎么办? 电脑上网很慢怎么办 笔记本网速太慢怎么办 ns下载特别慢怎么办 人人视频弹广告怎么办 忘记华为账号密码怎么办 手机电源键不灵怎么办