PriorityQueue は「優先度の高いものから順に取り出せるキュー」
PriorityQueue は、普通のキューと違って
「入れた順」ではなく「優先度の高い順(または小さい順)」で取り出せるキュー です。
イメージとしては、
受付で番号札を配るのではなく、
「重症度が高い人から診る救急外来」のようなものです。
業務では、
「優先度付きのタスク処理」
「スコアの高いものから順に処理」
「最小値・最大値を効率よく取り出したい」
といった場面で使えます。
基本:PriorityQueue は「最小値(または最大値)を効率よく取り出す箱」
最も小さい値から取り出す例(デフォルト)
PriorityQueue は、デフォルトでは「自然順序(昇順)」で並びます。
つまり、一番小さい値が先に出てくる キューです。
import java.util.PriorityQueue;
import java.util.Queue;
public class BasicPriorityQueue {
public static void main(String[] args) {
Queue<Integer> queue = new PriorityQueue<>();
queue.offer(30);
queue.offer(10);
queue.offer(20);
System.out.println(queue.poll()); // 10
System.out.println(queue.poll()); // 20
System.out.println(queue.poll()); // 30
System.out.println(queue.poll()); // null(空)
}
}
Javaここでの重要ポイントは二つです。
一つ目は、「入れた順番(30, 10, 20)とは関係なく、10 → 20 → 30 の順で出てくる」ことです。PriorityQueue は内部でヒープ構造を持っていて、
「最小値(または最大値)を素早く取り出す」ことに特化 しています。
二つ目は、「poll() は空のときに null を返す」ことです。remove() だと空のときに例外になるので、
「空かもしれない」前提なら poll() を使う方が安全です。
重要ポイント:PriorityQueue は「全体がソートされているわけではない」
イテレーションすると「バラバラ」に見える理由
PriorityQueue の中身を for-each で回すと、
「ソートされていないように見える」ことがあります。
PriorityQueue<Integer> queue = new PriorityQueue<>();
queue.offer(30);
queue.offer(10);
queue.offer(20);
System.out.println(queue); // [10, 30, 20] など、順番がバラバラに見えることがある
Javaここで大事なのは、
「PriorityQueue が保証するのは“先頭(取り出し対象)が最小(または最大)であること”だけ
という点です。
内部構造はヒープなので、
「全体がきれいにソートされている」わけではありません。
「小さい順に全部取り出したい」なら、poll() を繰り返すのが正しい使い方です。
while (!queue.isEmpty()) {
System.out.println(queue.poll());
}
JavaComparator を使って「優先度のルール」を自分で決める
大きい値から取り出したい(最大値優先)
デフォルトは「最小値優先」ですが、
「大きい値から取り出したい」 場面もよくあります。
その場合は、Comparator を渡してあげます。
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
public class MaxPriorityQueue {
public static void main(String[] args) {
Queue<Integer> queue = new PriorityQueue<>(Comparator.reverseOrder());
queue.offer(30);
queue.offer(10);
queue.offer(20);
System.out.println(queue.poll()); // 30
System.out.println(queue.poll()); // 20
System.out.println(queue.poll()); // 10
}
}
Javaここでの重要ポイントは、
「PriorityQueue は“優先度のルール”を Comparator で自由に変えられる」
ということです。
昇順・降順だけでなく、
「スコアの高いタスク」「締切が早いタスク」など、
業務ロジックに合わせた優先度を定義できます。
業務っぽい例:優先度付きタスクキュー
「優先度の高いタスクから処理する」キューを作る
タスクに「優先度」を持たせて、
優先度の高いものから処理する例を考えます。
import java.util.PriorityQueue;
import java.util.Queue;
public class TaskQueue {
static class Task {
final int priority; // 数字が大きいほど優先度高いとする
final String name;
Task(int priority, String name) {
this.priority = priority;
this.name = name;
}
@Override
public String toString() {
return "Task{" + priority + ", " + name + "}";
}
}
public static void main(String[] args) {
Queue<Task> queue = new PriorityQueue<>(
(a, b) -> Integer.compare(b.priority, a.priority) // priority の大きい順
);
queue.offer(new Task(1, "低優先度"));
queue.offer(new Task(3, "高優先度"));
queue.offer(new Task(2, "中優先度"));
while (!queue.isEmpty()) {
System.out.println(queue.poll());
}
// Task{3, 高優先度}
// Task{2, 中優先度}
// Task{1, 低優先度}
}
}
Javaここで深掘りしたいポイントは三つです。
一つ目は、「PriorityQueue<Task> に対して、Comparator で“優先度の比較ルール”を渡している」ことです。(a, b) -> Integer.compare(b.priority, a.priority) で「priority の大きい順」を定義しています。
二つ目は、「タスクの追加順ではなく、優先度で処理順が決まる」ことです。
業務では、「緊急度の高いジョブから処理したい」場面でそのまま使えます。
三つ目は、「Task 自体を Comparable にしてもよい」が、
業務では「比較ルールを外から差し替えたい」ことも多いので、Comparator を渡すスタイルを覚えておくと応用が効く、という点です。
締切(deadline)で優先度を決める例
「締切が早いタスクから処理する」
今度は、「締切が早いタスクほど優先度が高い」キューを考えます。
import java.time.Instant;
import java.util.PriorityQueue;
import java.util.Queue;
public class DeadlineTaskQueue {
static class Task {
final Instant deadline;
final String name;
Task(Instant deadline, String name) {
this.deadline = deadline;
this.name = name;
}
@Override
public String toString() {
return "Task{" + deadline + ", " + name + "}";
}
}
public static void main(String[] args) {
Queue<Task> queue = new PriorityQueue<>(
(a, b) -> a.deadline.compareTo(b.deadline) // 早い締切が先
);
queue.offer(new Task(Instant.parse("2025-03-01T00:00:00Z"), "3月1日まで"));
queue.offer(new Task(Instant.parse("2025-02-01T00:00:00Z"), "2月1日まで"));
queue.offer(new Task(Instant.parse("2025-04-01T00:00:00Z"), "4月1日まで"));
while (!queue.isEmpty()) {
System.out.println(queue.poll());
}
}
}
Javaここでのポイントは、
「PriorityQueue は“何を優先するか”を自由に設計できる」
という感覚を持つことです。
数値でも日付でも、複数フィールドの組み合わせでも、Comparator で比較ルールを書ければ、そのまま優先度付きキューになります。
PriorityQueue を使うときの注意点
スレッドセーフではない、イテレーション順は信用しない
PriorityQueue を使うときに、特に意識してほしい点が二つあります。
一つ目は、「PriorityQueue はスレッドセーフではない」ことです。
複数スレッドから同時に offer / poll するなら、
外側で同期を取るか、PriorityBlockingQueue などのスレッドセーフ版を検討します。
二つ目は、「イテレーション順は“優先度順”とは限らない」ことです。for-each で回したときの順番は内部構造次第で、
「最小(最大)から順に並んでいる」とは限りません。
「優先度順に処理したいなら、必ず poll() を繰り返す」
というルールを覚えておいてください。
まとめ:PriorityQueue利用で身につけてほしい感覚
PriorityQueue は、
単なる「ちょっと変わったキュー」ではなく、
「優先度付きで要素を取り出すための、業務向きの道具」 です。
デフォルトでは「最小値優先」だが、Comparator で自由に優先度ルールを決められる。
「全体がソートされている」のではなく、「先頭(取り出し対象)だけが最小/最大」であることを保証する。
優先度付きタスクキューや、締切順処理など、「何を先に処理すべきか」が明確な場面で真価を発揮する。
スレッドセーフではないので、マルチスレッドなら PriorityBlockingQueue なども視野に入れる。
あなたのコードのどこかに、
「List を sort してから先頭を取り出す」処理を何度も繰り返している箇所があれば、
そこを一度「PriorityQueue に置き換えられないか?」という目で眺めてみてください。
それが、「優先度」という軸でコレクションを選べるようになる、いい一歩になります。
