導入
システム開発において、タスクの順次処理やメッセージのバッファリングといった「順番待ち」のロジックは頻出します。JavaのListで管理しようとして、remove(0)を繰り返してパフォーマンスを低下させてしまった経験はありませんか?『java.util.Queue』を正しく使い分けることで、コードの可読性が上がるだけでなく、計算量の最適化とスレッドセーフな実装が可能になります。本記事では、実務で必須となるQueueの基礎と使いどころを解説します。
基礎知識
Queue(キュー)は、FIFO(First-In-First-Out:先入れ先出し)を実現するためのデータ構造です。JavaのCollectionフレームワークにおいて、Queueは「要素の追加・削除・参照」に対してそれぞれ2種類のメソッドを提供しています。
1. 成功時に値を返す(失敗時に例外を投げない):offer, poll, peek
2. 成功時に値を返す(失敗時に例外を投げる):add, remove, element
実務では、例外を発生させず戻り値(nullやfalse)で制御できる「1」のグループ(offer/poll/peek)を使用するのが推奨されます。
実装/解決策
Queueを扱う際は、用途に応じて実装クラスを選択することが重要です。
・LinkedList: 一般的なキューとして利用。
・ArrayDeque: 配列ベースで、メモリ効率が良く高速。単なるQueueとして使うならこちらが推奨。
・PriorityQueue: 優先度付きキュー。自然順序付けやComparatorに従って並び替えます。
サンプルプログラム
以下は、タスク処理を想定したArrayDequeの実装例です。
import java.util.ArrayDeque;
import java.util.Queue;
public class TaskProcessor {
public static void main(String[] args) {
// ArrayDequeは高速かつメモリ効率が良いので、基本はこちらを選択
Queue
// キューへの追加 (addよりも成功判定が容易なofferが推奨)
taskQueue.offer(“タスク1: ログ出力”);
taskQueue.offer(“タスク2: DB保存”);
taskQueue.offer(“タスク3: メール送信”);
// キューが空になるまで処理を継続
while (!taskQueue.isEmpty()) {
// pollは先頭要素を取り出し、キューが空ならnullを返す
String task = taskQueue.poll();
if (task != null) {
System.out.println(“実行中: ” + task);
}
}
}
}
応用・注意点
1. スレッドセーフな実装: 複数のスレッドから同時にアクセスする場合、上記のクラスでは不十分です。その際は java.util.concurrent パッケージの BlockingQueue(LinkedBlockingQueueなど)を使用してください。これは待機処理を内包しており、プロデューサー・コンシューマー・パターンの実装に最適です。
2. Sequenced Collectionsとの関係: Java 21から導入されたSequenced Collectionsにより、Dequeなどは先頭や末尾へのアクセスがより直感的に行えるようになりました。必要に応じて双方向キューとしての活用も検討しましょう。
3. nullの禁止: 実装によっては、Queueにnullを挿入することが禁止されています(特にPriorityQueueなど)。予期せぬNullPointerExceptionを防ぐため、常にバリデーション済みのオブジェクトのみを投入する設計を心がけてください。

コメント