Java PriorityQueue

以下內容轉載至PriorityQueue詳解

概念

PriorityQueue 一個基于優(yōu)先級的無界優(yōu)先級隊列备燃。優(yōu)先級隊列的元素按照其自然順序進行排序,或者根據(jù)構造隊列時提供的 Comparator 進行排序,具體取決于所使用的構造方法少态。該隊列不允許使用 null 元素也不允許插入不可比較的對象(沒有實現(xiàn)Comparable接口的對象)怀偷。
PriorityQueue 隊列的頭指排序規(guī)則最小那哥元素鄙皇。如果多個元素都是最小值則隨機選一個先巴。
PriorityQueue 是一個無界隊列其爵,但是初始的容量(實際是一個Object[]),隨著不斷向優(yōu)先級隊列添加元素伸蚯,其容量會自動擴容摩渺,無需指定容量增加策略的細節(jié)。

基本使用

PriorityQueue使用跟普通隊列一樣剂邮,唯一區(qū)別是PriorityQueue會根據(jù)排序規(guī)則決定誰在隊頭摇幻,誰在隊尾。
往隊列中添加可比較的對象String

public class App {
    public static void main(String[] args) {
        PriorityQueue<String> q = new PriorityQueue<String>();
        //入列
        q.offer("1");
        q.offer("2");
        q.offer("5");
        q.offer("3");
        q.offer("4");

        //出列
        System.out.println(q.poll());  //1
        System.out.println(q.poll());  //2
        System.out.println(q.poll());  //3
        System.out.println(q.poll());  //4
        System.out.println(q.poll());  //5
    }
}

觀察打印結果, 入列:12534绰姻, 出列是12345枉侧, 也是說出列時做了相關判斷,將最小的值返回狂芋。默認情況下PriorityQueue使用自然排序法榨馁,最小元素先出列。
自定義排序規(guī)則

public class Student {
    private String name;  //名字
    private int score;    //分數(shù)
   //省略getter/setter
}

public class App {
    public static void main(String[] args) {
        //通過改造器指定排序規(guī)則
        PriorityQueue<Student> q = new PriorityQueue<Student>(new Comparator<Student>() {
            public int compare(Student o1, Student o2) {
                //按照分數(shù)低到高帜矾,分數(shù)相等按名字
                if(o1.getScore() == o2.getScore()){
                    return o1.getName().compareTo(o2.getName());
                }
                return o1.getScore() - o2.getScore();
            }
        });
        //入列
        q.offer(new Student("dafei", 20));
        q.offer(new Student("will", 17));
        q.offer(new Student("setf", 30));
        q.offer(new Student("bunny", 20));

        //出列
        System.out.println(q.poll());  //Student{name='will', score=17}
        System.out.println(q.poll());  //Student{name='bunny', score=20}
        System.out.println(q.poll());  //Student{name='dafei', score=20}
        System.out.println(q.poll());  //Student{name='setf', score=30}
    }
}

PriorityQueue優(yōu)先級規(guī)則可以由我們根據(jù)具體需求而定制翼虫, 方式有2種:
1>添加元素自身實現(xiàn)了Comparable接口,確保元素是可排序的對象
2>如果添加元素沒有實現(xiàn)Comparable接口屡萤,可以在創(chuàng)建PriorityQueue隊列時直接指定比較器珍剑。

源碼解析

PriorityQueue 是怎么實現(xiàn)優(yōu)先級隊列的呢?

public class PriorityQueue<E> extends AbstractQueue<E>
    implements java.io.Serializable {
    transient Object[] queue;    //隊列容器死陆, 默認是11
    private int size = 0;  //隊列長度
    private final Comparator<? super E> comparator;  //隊列比較器招拙, 為null使用自然排序
    //....
}

入列

    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;
        else
            siftUp(i, e); //
        return true;
    }

    private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);   //指定比較器
        else
            siftUpComparable(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;
    }

    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;
    }

這里自作簡單比較迫像,使用選擇排序法將入列的元素放左邊或者右邊.
從源碼上看PriorityQueue的入列操作并沒對所有加入的元素進行優(yōu)先級排序。僅僅保證數(shù)組第一個元素是最小的即可瞳遍。
出列

    public E poll() {
        if (size == 0)
            return null;
        int s = --size;
        modCount++;
        E result = (E) queue[0];
        E x = (E) queue[s];
        queue[s] = null;
        if (s != 0)
            siftDown(0, x);
        return result;
    }

    private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);  //指定比較器
        else
            siftDownComparable(k, x);    //默認比較器
    }

private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>)x;
        int half = size >>> 1;        // loop while a non-leaf
        while (k < half) {
            int child = (k << 1) + 1; // assume left child is least
            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;
    }

    @SuppressWarnings("unchecked")
    private void siftDownUsingComparator(int k, E x) {
        int half = size >>> 1;
        while (k < half) {
            int child = (k << 1) + 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;
            k = child;
        }
        queue[k] = x;
    }

上面源碼闻妓,當?shù)谝粋€元素出列之后,對剩下的元素進行再排序掠械,挑選出最小的元素排在數(shù)組第一個位置由缆。

通過上面源碼,也可看出PriorityQueue并不是線程安全隊列猾蒂,因為offer/poll都沒有對隊列進行鎖定均唉,所以,如果要擁有線程安全的優(yōu)先級隊列肚菠,需要額外進行加鎖操作舔箭。

總結

1>PriorityQueue是一種無界的,線程不安全的隊列
2>PriorityQueue是一種通過數(shù)組實現(xiàn)的蚊逢,并擁有優(yōu)先級的隊列
3>PriorityQueue存儲的元素要求必須是可比較的對象层扶, 如果不是就必須明確指定比較器

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市烙荷,隨后出現(xiàn)的幾起案子镜会,更是在濱河造成了極大的恐慌,老刑警劉巖终抽,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件戳表,死亡現(xiàn)場離奇詭異桶至,居然都是意外死亡,警方通過查閱死者的電腦和手機匾旭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門镣屹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人季率,你說我怎么就攤上這事野瘦。” “怎么了飒泻?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵鞭光,是天一觀的道長。 經(jīng)常有香客問我泞遗,道長惰许,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任史辙,我火速辦了婚禮汹买,結果婚禮上,老公的妹妹穿的比我還像新娘聊倔。我一直安慰自己晦毙,他們只是感情好,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布耙蔑。 她就那樣靜靜地躺著见妒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪甸陌。 梳的紋絲不亂的頭發(fā)上须揣,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音钱豁,去河邊找鬼耻卡。 笑死,一個胖子當著我的面吹牛牲尺,可吹牛的內容都是我干的卵酪。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼谤碳,長吁一口氣:“原來是場噩夢啊……” “哼凛澎!你這毒婦竟也來了?” 一聲冷哼從身側響起估蹄,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎沫换,沒想到半個月后臭蚁,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體最铁,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年垮兑,在試婚紗的時候發(fā)現(xiàn)自己被綠了冷尉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡系枪,死狀恐怖雀哨,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情私爷,我是刑警寧澤雾棺,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站衬浑,受9級特大地震影響捌浩,放射性物質發(fā)生泄漏。R本人自食惡果不足惜工秩,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一尸饺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧助币,春花似錦浪听、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至倍谜,卻和暖如春迈螟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背尔崔。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工答毫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人季春。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓洗搂,卻偏偏與公主長得像,于是被迫代替她去往敵國和親载弄。 傳聞我的和親對象是個殘疾皇子耘拇,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內容

  • JDK 10.0.2 前段時間在網(wǎng)上刷題,碰到一個求中位數(shù)的題宇攻,看到有網(wǎng)友使用PriorityQueue來實現(xiàn)惫叛,感...
    哦00閱讀 1,178評論 0 0
  • 問:談談你對二叉堆數(shù)據(jù)結構的理解及在 PriorityQueue 中的實現(xiàn)? 答:這算是一道比較有深度的問題了逞刷,要...
    Little丶Jerry閱讀 1,741評論 0 2
  • ava中PriorityQueue通過二叉小頂堆實現(xiàn)嘉涌,可以用一棵完全二叉樹表示妻熊。本文從Queue接口函數(shù)出發(fā),結合...
    java菜閱讀 1,939評論 0 0
  • 回憶是一種淡淡的痛 改變不了這個世界的我仑最,曾經(jīng)的傷痛和苦悶讓我身心疲憊扔役。我流著淚,靜靜地躺在床上警医,細數(shù)前塵亿胸。不時,...
    乃毅閱讀 158評論 0 3
  • 1.收獲: 學習了靜好老師的“正面管教家長課程”以后预皇,感覺自己最大的收獲是侈玄,在跟孩子相處中,再遇到同樣的情境(當然...
    Amber冬月閱讀 393評論 0 0