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

詳解Java中PriorityQueue的作用和源碼實現(xiàn)

 更新時間:2024年02月04日 08:33:53   作者:一燈架構(gòu)  
這篇文章主要為大家詳細介紹了Java中阻塞隊列PriorityQueue的作用和源碼實現(xiàn)的相關(guān)知識,文中的示例代碼講解詳細,需要的小伙伴可以了解下

引言

前面文章我們講解了ArrayBlockingQueueLinkedBlockingQueue源碼,這篇文章開始講解PriorityQueue源碼。從名字上就能看到ArrayBlockingQueue是基于數(shù)組實現(xiàn)的,而LinkedBlockingQueue是基于鏈表實現(xiàn),而PriorityQueue是基于什么數(shù)據(jù)結(jié)構(gòu)實現(xiàn)的,看不出來,好像是實現(xiàn)了優(yōu)先級的隊列。

由于PriorityQueue跟前幾個阻塞隊列不一樣,并沒有實現(xiàn)BlockingQueue接口,只是實現(xiàn)了Queue接口,Queue接口中定義了幾組放數(shù)據(jù)和取數(shù)據(jù)的方法,來滿足不同的場景。

操作拋出異常返回特定值
放數(shù)據(jù)add()offer()
取數(shù)據(jù)(同時刪除數(shù)據(jù))remove()poll()
查看數(shù)據(jù)(不刪除)element()peek()

這兩組方法的區(qū)別是:

  • 當隊列滿的時候,再次添加數(shù)據(jù),add()會拋出異常,offer()會返回false。
  • 當隊列為空的時候,再次取數(shù)據(jù),remove()會拋出異常,poll()會返回null。

PriorityQueue也會有針對這幾組放數(shù)據(jù)和取數(shù)據(jù)方法的具體實現(xiàn)。

類結(jié)構(gòu)

先看一下PriorityQueue類里面有哪些屬性:

public class PriorityQueue<E> 
        extends AbstractQueue<E>
        implements java.io.Serializable {

    /**
     * 數(shù)組初始容量大小
     */
    private static final int DEFAULT_INITIAL_CAPACITY = 11;

    /**
     * 數(shù)組,用于存儲元素
     */
    transient Object[] queue; // non-private to simplify nested class access

    /**
     * 元素個數(shù)
     */
    private int size = 0;

    /**
     * 比較器,用于排序元素優(yōu)先級
     */
    private final Comparator<? super E> comparator;

}

可以看出PriorityQueue底層是基于數(shù)組實現(xiàn)的,使用Object[]數(shù)組存儲元素,并且定義了比較器comparator,用于排序元素的優(yōu)先級。

初始化

PriorityQueue常用的初始化方法有4個:

  • 無參構(gòu)造方法
  • 指定容量大小的有參構(gòu)造方法
  • 指定比較器的有參構(gòu)造方法
  • 同時指定容量和比較器的有參構(gòu)造方法
/**
 * 無參構(gòu)造方法
 */
PriorityQueue<Integer> blockingQueue1 = new PriorityQueue<>();

/**
 * 指定容量大小的構(gòu)造方法
 */
PriorityQueue<Integer> blockingQueue2 = new PriorityQueue<>(10);

/**
 * 指定比較器的有參構(gòu)造方法
 */
PriorityQueue<Integer> blockingQueue3 = new PriorityQueue<>(Integer::compareTo);

/**
 * 同時指定容量和比較器的有參構(gòu)造方法
 */
PriorityQueue<Integer> blockingQueue4 = new PriorityQueue<>(10, Integer::compare);

再看一下對應(yīng)的源碼實現(xiàn):

/**
 * 無參構(gòu)造方法
 */
public PriorityQueue() {
    // 使用默認容量大小11,不指定比較器
    this(DEFAULT_INITIAL_CAPACITY, null);
}

/**
 * 指定容量大小的構(gòu)造方法
 */
public PriorityQueue(int initialCapacity) {
    this(initialCapacity, null);
}

/**
 * 指定比較器的有參構(gòu)造方法
 */
public PriorityQueue(Comparator<? super E> comparator) {
    this(DEFAULT_INITIAL_CAPACITY, comparator);
}

/**
 * 同時指定容量和比較器的有參構(gòu)造方法
 */
public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) {
    if (initialCapacity < 1) {
        throw new IllegalArgumentException();
    }
    this.queue = new Object[initialCapacity];
    this.comparator = comparator;
}

可以看出PriorityQueue的無參構(gòu)造方法使用默認的容量大小11,直接初始化數(shù)組,并且沒有指定比較器。

放數(shù)據(jù)源碼

放數(shù)據(jù)的方法有2個:

操作拋出異常返回特定值
放數(shù)據(jù)add()offer()

offer方法源碼

先看一下offer()方法源碼,其他放數(shù)據(jù)方法邏輯也是大同小異,都是在鏈表尾部插入。 offer()方法在隊列滿的時候,會直接返回false,表示插入失敗。

/**
 * offer方法入口
 *
 * @param e 元素
 * @return 是否插入成功
 */
public boolean offer(E e) {
    // 1. 判空,傳參不允許為null
    if (e == null) {
        throw new NullPointerException();
    }
    modCount++;
    int i = size;
    // 2. 當數(shù)組滿的時候,執(zhí)行擴容
    if (i >= queue.length) {
        grow(i + 1);
    }
    size = i + 1;
    // 3. 如果是第一次插入,就直接把元素插入到數(shù)組頭部
    if (i == 0) {
        queue[0] = e;
    } else {
        // 4. 如果不是第一次插入,就找個合適的位置插入(需要保證插入后數(shù)組有序)
        siftUp(i, e);
    }
    return true;
}

offer()方法邏輯也很簡單,先判斷是否需要擴容,如果需要擴容先執(zhí)行擴容邏輯,然后把元素插入到數(shù)組中。如果是第一次插入,就直接把元素插入到數(shù)組頭部。如果不是,就找個合適的位置插入,需要保證插入后數(shù)組仍是有序的。 再看一下擴容的源碼:

/**
 * 擴容
 */
private void grow(int minCapacity) {
    int oldCapacity = queue.length;
    // 1. 如果原數(shù)組容量小于64,就執(zhí)行2倍擴容,否則執(zhí)行1.5擴容
    int newCapacity = oldCapacity + 
            ((oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1));
    // 2. 校驗最大容量不能超過Integer最大值
    if (newCapacity - MAX_ARRAY_SIZE > 0) {
        newCapacity = hugeCapacity(minCapacity);
    }
    // 3. 直接擴容后新數(shù)組賦值給原數(shù)組
    queue = Arrays.copyOf(queue, newCapacity);
}

擴容的源碼設(shè)計充滿了作者的巧思,在數(shù)組容量較小的時候,為了避免頻繁擴容,就采用2倍擴容法。在數(shù)組容量較大的時候,為了避免擴容后浪費空間,就采用1.5倍擴容法。

PriorityQueue為了快速的插入和刪除,采用了最小堆,而不是直接使用有序數(shù)組,這樣既可以保證插入和刪除的時間復(fù)雜度都是O(logn),又能避免移動過多元素。

最小堆的定義: 除葉子節(jié)點外,每個節(jié)點的值都小于等于左右子節(jié)點的值。

下面就是一個簡單的最小堆和映射數(shù)組:

再看一下siftUp()方法源碼,是怎么保證插入元素,數(shù)組仍是有序的? 其實就是循環(huán)跟父節(jié)點比較元素大小,找個合適的位置插入。

// 把元素插入到合適的位置
private void siftUp(int k, E x) {
    // 1. 如果初始化的時候,自定義了比較器,就使用自定義比較器的插入方法,否則使用默認的。
    if (comparator != null) {
        siftUpUsingComparator(k, x);
    } else {
        siftUpComparable(k, x);
    }
}

// 自定義比較器的插入方法
private void siftUpUsingComparator(int k, E x) {
    while (k > 0) {
        // 1. 找到父節(jié)點
        int parent = (k - 1) >>> 1;
        Object e = queue[parent];
        // 2. 如果當前節(jié)點元素比父節(jié)點的元素小,就把父節(jié)點元素向下移動(給當前元素騰出位置)
        if (comparator.compare(x, (E) e) >= 0) {
            break;
        }
        queue[k] = e;
        k = parent;
    }
    // 3. 把當前元素插入到父節(jié)點的位置
    queue[k] = x;
}

// 默認的插入方法
private void siftUpComparable(int k, E x) {
    // 1. 使用默認比較器
    Comparable<? super E> key = (Comparable<? super E>) x;
    while (k > 0) {
        // 2. 找到父節(jié)點
        int parent = (k - 1) >>> 1;
        Object e = queue[parent];
        // 3. 如果當前節(jié)點元素比父節(jié)點的元素小,就把父節(jié)點元素向下移動(給當前元素騰出位置)
        if (key.compareTo((E) e) >= 0) {
            break;
        }
        queue[k] = e;
        k = parent;
    }
    // 4. 把當前元素插入到父節(jié)點的位置
    queue[k] = key;
}

再看一下add()方法源碼:

add方法源碼

add()方法底層直接調(diào)用的是offer()方法,作用相同。

/**
 * add方法入口
 *
 * @param e 元素
 * @return 是否添加成功
 */
public boolean add(E e) {
    return offer(e);
}

彈出數(shù)據(jù)源碼

彈出數(shù)據(jù)(取出數(shù)據(jù)并刪除)的方法有2個:

操作拋出異常返回特定值
取數(shù)據(jù)(同時刪除數(shù)據(jù))remove()poll()

poll方法源碼

看一下poll()方法源碼,其他方取數(shù)據(jù)法邏輯大同小異,都是從數(shù)組頭部彈出元素。 poll()方法在彈出元素的時候,如果隊列為空,直接返回null,表示彈出失敗。

/**
 * poll方法入口
 */
public E poll() {
    // 1. 如果數(shù)組為空,返回null
    if (size == 0) {
        return null;
    }
    int s = --size;
    modCount++;
    // 2. 暫存數(shù)組頭節(jié)點,最后返回
    E result = (E) queue[0];
    // 3. 暫存數(shù)組尾節(jié)點,調(diào)整最小堆的時候,需要上移
    E x = (E) queue[s];
    // 4. 刪除尾節(jié)點
    queue[s] = null;
    // 5. 調(diào)整最小堆
    if (s != 0) {
        siftDown(0, x);
    }
    return result;
}

remove方法源碼

再看一下remove()方法源碼,如果隊列為空,remove()會拋出異常。

/**
 * remove方法入口
 */
public E remove() {
    // 1. 直接調(diào)用poll方法
    E x = poll();
    // 2. 如果取到數(shù)據(jù),直接返回,否則拋出異常
    if (x != null) {
        return x;
    } else {
        throw new NoSuchElementException();
    }
}

查看數(shù)據(jù)源碼

再看一下查看數(shù)據(jù)源碼,查看數(shù)據(jù),并不刪除數(shù)據(jù)。

操作拋出異常返回特定值
查看數(shù)據(jù)(不刪除)element()peek()

peek方法源碼

先看一下peek()方法源碼,如果數(shù)組為空,直接返回null。

/**
 * peek方法入口
 */
public E peek() {
    // 返回數(shù)組頭節(jié)點
    return (size == 0) ? null : (E) queue[0];
}

element方法源碼

再看一下element()方法源碼,如果隊列為空,則拋出異常。

/**
 * element方法入口
 */
public E element() {
    // 1. 調(diào)用peek方法查詢數(shù)據(jù)
    E x = peek();
    // 2. 如果查到數(shù)據(jù),直接返回
    if (x != null) {
        return x;
    } else {
        // 3. 如果沒找到,則拋出異常
        throw new NoSuchElementException();
    }
}

總結(jié)

這篇文章講解了PriorityQueue阻塞隊列的核心源碼,了解到PriorityQueue隊列具有以下特點:

  • PriorityQueue實現(xiàn)了Queue接口,提供了兩組放數(shù)據(jù)和讀數(shù)據(jù)的方法,來滿足不同的場景。
  • PriorityQueue底層基于數(shù)組實現(xiàn),按照最小堆存儲,實現(xiàn)了高效的插入和刪除。
  • PriorityQueue初始化的時候,可以指定數(shù)組長度和自定義比較器。
  • PriorityQueue初始容量是11,當數(shù)組容量小于64,采用2倍擴容,否則采用1.5擴容。
  • PriorityQueue每次都是從數(shù)組頭節(jié)點取元素,取之后需要調(diào)整最小堆。

今天一起分析了PriorityQueue隊列的源碼,可以看到PriorityQueue的源碼非常簡單,沒有什么神秘復(fù)雜的東西,下篇文章再一起接著分析其他的阻塞隊列源碼。

以上就是詳解Java中PriorityQueue的作用和源碼實現(xiàn)的詳細內(nèi)容,更多關(guān)于Java PriorityQueue的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot如何讀取war包jar包和Resource資源

    SpringBoot如何讀取war包jar包和Resource資源

    這篇文章主要介紹了SpringBoot如何讀取war包jar包和Resource資源,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-01-01
  • spring mvc+localResizeIMG實現(xiàn)HTML5端圖片壓縮上傳

    spring mvc+localResizeIMG實現(xiàn)HTML5端圖片壓縮上傳

    這篇文章主要為大家詳細介紹了使用spring mvc+localResizeIMG實現(xiàn)HTML5端圖片壓縮上傳,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • Java超詳細分析垃圾回收機制

    Java超詳細分析垃圾回收機制

    一個運行中的程序,?產(chǎn)生的對象是大量的,?如果對象不被繼續(xù)使用,?就會成為垃圾,?最后越堆越多,?最后占滿內(nèi)存,?所以我們要對這些垃圾進行回收,保持程序的正常運行
    2022-05-05
  • 詳解Nacos配置中心的實現(xiàn)

    詳解Nacos配置中心的實現(xiàn)

    Spring Cloud Alibaba 是阿里巴巴提供的一站式微服務(wù)開發(fā)解決方案。而 Nacos 作為 Spring Cloud Alibaba 的核心組件之一,提供了兩個非常重要的功能:注冊中心和配置中心,我們今天來了解和實現(xiàn)一下二者
    2022-08-08
  • Java多線程從基礎(chǔ)到高級應(yīng)用示例小結(jié)

    Java多線程從基礎(chǔ)到高級應(yīng)用示例小結(jié)

    本章介紹了Java多線程編程的基礎(chǔ)知識,包括線程的創(chuàng)建與管理、線程同步、線程通信和線程池,重點內(nèi)容包括線程的三種創(chuàng)建方式,通過本章的學習,讀者掌握了Java多線程編程的基本技能,感興趣的朋友跟隨小編一起看看吧
    2026-01-01
  • 如何使用IntelliJ IDEA的HTTP Client進行接口驗證

    如何使用IntelliJ IDEA的HTTP Client進行接口驗證

    這篇文章主要介紹了如何使用IntelliJ IDEA的HTTP Client進行接口驗證,本文給大家分享最新完美解決方案,感興趣的朋友跟隨小編一起看看吧
    2024-06-06
  • SpringCloud Config統(tǒng)一配置中心問題分析解決與客戶端動態(tài)刷新實現(xiàn)

    SpringCloud Config統(tǒng)一配置中心問題分析解決與客戶端動態(tài)刷新實現(xiàn)

    springcloud config是一個解決分布式系統(tǒng)的配置管理方案。它包含了 client和server兩個部分,server端提供配置文件的存儲、以接口的形式將配置文件的內(nèi)容提供出去,client端通過接口獲取數(shù)據(jù)、并依據(jù)此數(shù)據(jù)初始化自己的應(yīng)用
    2022-10-10
  • 深入理解java虛擬機的故障處理工具

    深入理解java虛擬機的故障處理工具

    大家都知道在給系統(tǒng)定位問題的時候,知識、經(jīng)驗是關(guān)鍵基礎(chǔ),數(shù)據(jù)是依據(jù),工具是運用知識處理數(shù)據(jù)的手段。Java開發(fā)人員可以在jdk安裝的bin目錄下找到除了java,javac以外的其他命令。這些命令主要是一些用于監(jiān)視虛擬機和故障處理的工具,下面來看看詳細的介紹。
    2016-11-11
  • Java數(shù)組的基本操作方法整理

    Java數(shù)組的基本操作方法整理

    這篇文章主要介紹了Java數(shù)組的基本操作方法整理,是Java入門學習中的基礎(chǔ)知識,需要的朋友可以參考下
    2015-08-08
  • JDK動態(tài)代理與CGLib動態(tài)代理的區(qū)別對比

    JDK動態(tài)代理與CGLib動態(tài)代理的區(qū)別對比

    今天小編就為大家分享一篇關(guān)于JDK動態(tài)代理與CGLib動態(tài)代理的區(qū)別對比,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-02-02

最新評論

台前县| 修武县| 平山县| 松滋市| 历史| 积石山| 莱阳市| 楚雄市| 天柱县| 雅安市| 康平县| 龙口市| 合山市| 大丰市| 页游| 海伦市| 陇西县| 惠东县| 娄烦县| 西华县| 西乡县| 浠水县| 阜康市| 公安县| 湖北省| 竹北市| 锡林郭勒盟| 定日县| 青田县| 余庆县| 定西市| 达日县| 鹤峰县| 锦州市| 东乡县| 文登市| 新野县| 和平县| 益阳市| 台州市| 伽师县|