最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JDK源碼之PriorityQueue解析

 更新時間:2017年04月25日 11:51:25   作者:_fred  
這篇文章主要為大家詳細介紹了JDK源碼之PriorityQueue,具有一定的參考價值,感興趣的小伙伴們可以參考一下

一.優(yōu)先隊列的應用

優(yōu)先隊列在程序開發(fā)中屢見不鮮,比如操作系統在進行進程調度時一種可行的算法是使用優(yōu)先隊列,當一個新的進程被fork()出來后,首先將它放到隊列的最后,而操作系統內部的Scheduler負責不斷地從這個優(yōu)先隊列中取出優(yōu)先級較高的進程執(zhí)行;爬蟲系統在執(zhí)行時往往也需要從一個優(yōu)先級隊列中循環(huán)取出高優(yōu)先級任務并進行抓取。可以想見,如果類似這樣的任務不適用優(yōu)先級進行劃分的話,系統必會出現故障,例如操作系統中低優(yōu)先級進程持續(xù)占用資源而高優(yōu)先級進程始終在隊列中等待。此外,優(yōu)先隊列在貪婪算法中也有一些應用。

二.優(yōu)先隊列的實現原理

優(yōu)先隊列的實現方式是使用二叉堆的結構,需要滿足以下兩條性質(Heap property),這里以小頂堆為例講解:

  1.任何結點的值都小于或等于其子節(jié)點的值。
  2.所有結點從上到下,從左到右填入,即一棵完全二叉樹。

基于這兩條規(guī)律,二叉堆在實現中往往會使用一個數組,下面我們研究一下JDK中二叉堆(優(yōu)先隊列)的實現。

三.優(yōu)先隊列在JDK中的實現方式

研究源碼最好的方式是debug,看每一步變量的變化,我們可以簡單寫一個Demo,debug進源碼一探究竟:

這里我們簡單地創(chuàng)建一個優(yōu)先隊列,向其中添加三個元素,我們可以在代碼第一行打一個斷點,如果您使用Eclipse編輯器的話,接下來可以按F5進入源碼中:

代碼運行到這里,PriorityQueue調用自己的一個重載構造器,第一個參數是數組默認大小,第二個是元素比較的Comparator,我們這里的Demo比較簡單,您在使用優(yōu)先隊列時可以選擇實現自己的Comparator。

 public PriorityQueue(int initialCapacity,
             Comparator<? super E> comparator) {
    // Note: This restriction of at least one is not actually needed,
    // but continues for 1.5 compatibility
    if (initialCapacity < 1)
      throw new IllegalArgumentException();
    this.queue = new Object[initialCapacity];
    this.comparator = comparator;
  }

接下來我們研究一下添加元素時的offer操作:

 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) 
      //第一次添加元素,直接放到第0個位置即可
      queue[0] = e;
    else
      //需要將元素放到最后,再做上濾操作
      siftUp(i, e);
    return true;
  }

我們逐行來解釋一下,首先offer方法判斷參數是否為空,不為空則對變量modCount自增,modCount記錄了隊列被修改的次數,接下來,判斷數組是否會越界,如果越界則通過grow進行擴容,接下來添加元素,如果當前元素為0個則直接將元素放到數組第一個位置,否則做一個siftUp的操作。

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

上面的代碼對隊列擴容,源碼中注釋也很清晰,首先判斷當前的數組大小是否足夠小(<64),如果足夠小則將大小擴充為2倍,否則將原大小加上50%。需要注意的是,這里最后做了一個大小是否溢出的判斷。

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
      throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
      Integer.MAX_VALUE :
      MAX_ARRAY_SIZE;
  }

如果需要擴容的大小已經<0了,顯然已經溢出了,在這里拋出了OutOfMemory的異常。

private void siftUpUsingComparator(int k, E x) {
    while (k > 0) {
      //計算父親節(jié)點的下標
      int parent = (k - 1) >>> 1;
      Object e = queue[parent];
      //與父節(jié)點進行比較
      if (comparator.compare(x, (E) e) >= 0)
        break;
      queue[k] = e;
      k = parent;
    }
    queue[k] = x;
  }

為了保證優(yōu)先隊列的性質1,在插入每個元素時都需要與該節(jié)點父親進行比較,找到其正確位置,有些數據結構書中,這個操作被稱為上濾(percolate up)。

入隊操作已經說完了,接下來是出隊操作,即poll()操作:

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

這個方法首先將數組第一個元素作為結果,(因為如果是小頂堆的話堆頂始終是最小元素),并將隊列的最后一個元素放到第一個位置,最后用siftDown做一些調整,保證隊列的性質,這個操作被稱為下濾(percolate down)。

 private void siftDownUsingComparator(int k, E x) {
 

    int half = size >>> 1;
    //這里k必須有孩子,故葉節(jié)點需要比較
    while (k < half) {
      //以下幾行代碼到較小的那個兒子,用變量c表示
      int child = (k << 1) + 1;
      //這里假設左兒子比較小
      Object c = queue[child];
      int right = child + 1;
      //左右兒子比較,如果右兒子小則將c賦值為右兒子
      if (right < size &&
        comparator.compare((E) c, (E) queue[right]) > 0)
        c = queue[child = right];
      //如果x比小兒子還小,說明k就是正確位置
      if (comparator.compare(x, (E) c) <= 0)
        break;
      queue[k] = c;
      k = child;
    }
    queue[k] = x;
  }

如上圖,下濾過程中k不斷與其兒子進行比較,如果滿足優(yōu)先隊列的順序性,則break出循環(huán)。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • Java可重入鎖的實現示例

    Java可重入鎖的實現示例

    在java中,可重入鎖分為兩種,即synchronized鎖以及ReentrantLock及其實現,文中通過示例代碼介紹的非常詳細,需要的朋友們下面隨著小編來一起學習學習吧
    2024-02-02
  • 從HelloWorld和文檔注釋開始入門Java編程

    從HelloWorld和文檔注釋開始入門Java編程

    這篇文章主要介紹了從HelloWorld和文檔注釋開始入門Java編程,涉及到Javadoc工具的使用,需要的朋友可以參考下
    2015-10-10
  • Spring Data JPA 復雜/多條件組合分頁查詢

    Spring Data JPA 復雜/多條件組合分頁查詢

    本文主要介紹了Spring Data JPA 復雜/多條件組合分頁查詢的相關資料。具有很好的參考價值。下面跟著小編一起來看下吧
    2017-04-04
  • 處理Log4j2不能打印行號的問題(AsyncLogger)

    處理Log4j2不能打印行號的問題(AsyncLogger)

    這篇文章主要介紹了處理Log4j2不能打印行號的問題(AsyncLogger),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • Java類初始化順序詳解

    Java類初始化順序詳解

    這篇文章主要介紹了Java類初始化順序詳解,java語言在使用過程中最先開始就是初始化,在工作中如果遇到什么問題需?要定位往往到最后也可能是初始化的問題,因此掌握初始化的順序很重要,需要的朋友可以參考下
    2023-08-08
  • Mybatis-plus如何通過反射實現動態(tài)排序不同字段功能

    Mybatis-plus如何通過反射實現動態(tài)排序不同字段功能

    這篇文章主要介紹了Mybatis-plus如何通過反射實現動態(tài)排序不同字段功能,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-02-02
  • Java中this的用法實例總結

    Java中this的用法實例總結

    JAVA中的this是一個非常重要的模塊,在編程中有非常重要的地位,擅長用this的人常??梢允钩绦蚋雍啙嵑头奖?下面這篇文章主要給大家介紹了關于Java中this用法的相關資料,需要的朋友可以參考下
    2022-08-08
  • Java實現的進制轉換工具類完整示例

    Java實現的進制轉換工具類完整示例

    這篇文章主要介紹了Java實現的進制轉換工具類,結合完整實例形式分析了Java實現二進制、十六進制、字符串、數組等相關轉換操作技巧,需要的朋友可以參考下
    2018-07-07
  • SpringBoot結合JWT登錄權限控制的實現

    SpringBoot結合JWT登錄權限控制的實現

    本文主要介紹了SpringBoot結合JWT登錄權限控制的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-07-07
  • SpringCloud-Config分布式配置代碼示例

    SpringCloud-Config分布式配置代碼示例

    這篇文章主要介紹了SpringCloud-Config分布式配置代碼示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-05-05

最新評論

平遥县| 隆昌县| 玉门市| 文化| 固安县| 佛山市| 迁西县| 定州市| 海淀区| 六盘水市| 安徽省| 高清| 巴林左旗| 资阳市| 定远县| 凤庆县| 铜川市| 宜阳县| 大丰市| 申扎县| 治县。| 湘西| 集贤县| 西乌| 靖州| 会东县| 库伦旗| 新干县| 安仁县| 潞西市| 贵南县| 阿拉尔市| 治县。| 吉安县| 乌鲁木齐市| 甘孜县| 陈巴尔虎旗| 上饶市| 航空| 扶沟县| 定州市|