HOWTO · Java

Java 最大優先順序佇列

本教程演示如何從 Java 中的優先順序佇列中獲取最大值。

本頁內容

在 Java 中建立最大優先佇列時,請建立一個搭配比較器的 PriorityQueue,讓比較器反轉值的自然排序。如此一來,佇列的頂端就是最大元素;重複呼叫 poll() 會依照從大到小的順序移除元素。Java 預設的 PriorityQueue 是最小堆積,因此若未提供比較器,頂端會是最小元素。

Java 中的優先佇列是什麼

優先佇列會依照優先順序取出元素,而非按照插入順序。它不是 FIFO 佇列:如果後插入的元素優先順序較高,就可能比其他元素先取出。使用自然排序時,優先順序依據值的 Comparable 順序;有其他排序需求時,則可由比較器指定。

Java 的 PriorityQueue 以堆積為基礎,能有效率地存取頂端元素。新增與移除元素需要對數時間;使用 peek() 查看頂端則需要常數時間。這個類別不是執行緒安全的。若要在並行環境中使用,可考慮 java.util.concurrent 中的 PriorityBlockingQueue,並留意它有不同的並行處理行為。

Java 的 PriorityQueue 如何排序元素

排序規則決定哪個元素位於佇列頂端。使用預設建構子時,整數與字串會採用自然排序,因此最小值位於頂端。比較器可以反轉這個順序,也可以比較自訂物件中的欄位。

堆積不會讓所有元素都維持排序後的順序。迭代器會走訪堆積的內部結構,因此迭代 PriorityQueue 不保證輸出結果已排序。若要查看目前的頂端,請使用 peek();若要依優先順序取得元素,則重複移除頂端。

為自然排序的值建立最大優先佇列

對整數值傳入一個以右運算元和左運算元進行比較的比較器即可。同樣的做法也適用於字串等其他具有自然排序的值。元素插入時,佇列會依照比較器安排頂端;不必先排序輸入值。

以下範例示範這種做法:

本節中的程式碼以及顯示的標籤或輸出仍為英文。

import java.util.PriorityQueue;

public class Main {
    public static void main(String[] args) {
        PriorityQueue<Integer> values = new PriorityQueue<>((left, right) -> {
            if (left < right) return 1;
            if (left > right) return -1;
            return 0;
        });
        values.add(20);
        values.add(10);
        values.add(30);

        System.out.println(values.poll());
        System.out.println(values.poll());
        System.out.println(values.poll());
    }
}

比較分支會反轉整數的自然排序,且不會以相減方式比較值;極大或極小的整數相減可能造成溢位。依照這個比較器,佇列頂端會是最大值。每次呼叫 poll() 都會移除頂端,因此清空佇列時會依照遞減順序取得值。

對字串而言,可以使用 right.compareTo(left) 反轉其自然字典順序。也就是說,「最大」由值型別的排序規則決定;除非比較器另有定義,否則它不代表最長字串或符合特定領域需求的排名。

依優先順序排列自訂物件

自訂物件可以依其中一個欄位排序。例如,若 Task 有整數 priority,可用明確的小於與大於分支,比較右側工作與左側工作的優先值;相等時則回傳零。這樣較大的優先值會排在頂端,而且不需要用算術相減比較。

比較器必須保持一致:若兩個物件的優先值被判定為相等,任一個都可能先取出。優先佇列不保證相同優先值的元素會依穩定順序排列。若應用程式需要明確決定同優先值工作項目的順序,可以加入次要比較條件,例如序號。

取得最大元素並避免排序上的陷阱

使用 peek() 查看最大值但不移除它;使用 poll() 移除並回傳目前的最大值。佇列為空時,peek() 和 poll() 都會回傳 null;請檢查回傳值是否為 null,或先檢查 isEmpty(),再使用取得的值。重複呼叫 poll(),直到回傳 null,就會依比較器順序清空佇列。

不要把迭代當成排序後的走訪方式,也不要說佇列本身已排序。比較器會決定頂端元素以及後續的移除順序。建立最大優先佇列時,請安全地反轉自然排序或欄位排序;若多個執行緒需要修改此結構,請使用支援並行操作的佇列實作。