導入: なぜDequeが重要なのか
Javaでプログラミングをしていると、単なる「リスト(List)」では不十分なケースに直面します。例えば、履歴機能のように「最新の操作を先頭に追加したい」場合や、待ち行列のように「先頭から取り出して末尾に追加する」処理です。java.util.Deque(Double Ended Queue)は、両端から要素を操作できる強力なインターフェースで、スタック(LIFO)やキュー(FIFO)の両方の性質を併せ持ちます。これを適切に使い分けることで、コードの可読性が向上し、予期せぬ例外の発生を未然に防ぐことができます。
基礎知識: Dequeの基本概念
Dequeは「両端キュー」と呼ばれ、先頭(First)と末尾(Last)の両方で要素の追加・削除・取得が可能です。重要なのは、各操作には「例外をスローするメソッド」と「特殊な値(nullやfalse)を返すメソッド」の2種類がある点です。
・例外をスローする系: addFirst, addLast, removeFirst, removeLast, getFirst, getLast
・特殊な値を返す系: offerFirst, offerLast, pollFirst, pollLast, peekFirst, peekLast
原則として、データが空である可能性がある場合は後者の「特殊な値を返す系」を使用するのが安全です。
実装/解決策: 適切なメソッドの選択
現場での実装では、以下の方針を推奨します。
1. 追加時:容量制限がある場合はoffer系、ない場合はadd系。
2. 取得時:空の可能性を考慮するならpeek系(nullが返る)、例外で制御したいならget系。
3. 削除時:空の可能性を考慮するならpoll系(nullが返る)、厳密に管理するならremove系。
サンプルプログラム
以下のコードは、Dequeの代表的な操作を示した実用例です。
import java.util.ArrayDeque;
import java.util.Deque;
public class DequeSample {
public static void main(String[] args) {
// ArrayDequeはDequeインターフェースの最も一般的な実装クラスです
Deque<String> deque = new ArrayDeque<>();
// 1. 要素の追加
deque.offerLast("タスクB");
deque.offerFirst("タスクA"); // 先頭に挿入
// 2. 要素の確認(例外を出さずにnullを返す)
System.out.println("先頭要素: " + deque.peekFirst()); // タスクA
// 3. 要素の取り出し(処理して削除)
while (!deque.isEmpty()) {
// pollFirstは空の場合nullを返すため安全
String task = deque.pollFirst();
System.out.println("実行中: " + task);
}
// 4. 空の状態で取り出そうとしても例外は発生しない
System.out.println("取り出し結果: " + deque.pollFirst()); // null
}
}
応用・注意点: 現場で役立つ補足
Dequeは「Sequenced Collections(Java 21から導入)」の一部であり、順序付けされたコレクションとしての統一的な扱いが可能になりました。
注意点として、Dequeの実装クラスにnullを挿入することは禁止されています。多くの実装(ArrayDequeなど)がnullを特殊な値として内部で利用しているため、nullを入れようとするとNullPointerExceptionが発生します。また、スレッドセーフな実装が必要な場合は、java.util.concurrent.LinkedBlockingDequeを選択してください。
現場のコードレビューでは、特に「removeFirst()を使っていて、空の時にクラッシュしていないか」をチェックすることが重要です。安全性を高めるために、極力pollやpeekを活用する習慣をつけましょう。

コメント