導入
Javaでスタックやキューを扱う際、つい「Stackクラス」や「LinkedList」を使用していませんか?実は、スタックやキューの用途であれば、java.util.ArrayDequeを使用するのが、パフォーマンスとメモリ効率の観点から最も賢い選択です。本記事では、なぜArrayDequeが実務で推奨されるのか、その理由と実装のポイントを解説します。
基礎知識
ArrayDequeは、名前の通り「配列(Array)」をベースにした「両端キュー(Double Ended Queue)」です。
従来のjava.util.Stackクラスは、古い設計の名残で全てのメソッドが同期化(synchronized)されており、単一スレッド環境ではオーバーヘッドが大きくなります。また、LinkedListはノードごとにオブジェクトを生成するため、メモリ効率が悪化しやすい傾向があります。
一方、ArrayDequeはサイズ変更可能な配列を利用しており、要素の追加・削除が極めて高速です。スタック(LIFO)として使う場合はDequeインターフェースのpush/popメソッドを、キュー(FIFO)として使う場合はoffer/pollメソッドを使用します。
実装/解決策
ArrayDequeを扱う際は、以下の点に注意してください。
1. nullを格納できない: ArrayDequeはnullの追加を許可しません。nullを許容する必要がある場合はLinkedListを検討してください。
2. 初期容量の指定: 要素数が予測できる場合は、コンストラクタで初期容量を指定することで、配列のコピー(リサイズ処理)を減らし、パフォーマンスをさらに向上させることができます。
サンプルプログラム
以下のコードは、ArrayDequeを使ってスタックとキューの両方の振る舞いを確認する実用的な例です。
import java.util.ArrayDeque;
import java.util.Deque;
public class DequeExample {
public static void main(String[] args) {
// スタックとして利用する場合 (LIFO: 後入れ先出し)
Deque
stack.push(“タスク1”);
stack.push(“タスク2”);
System.out.println(“スタックから取り出し: ” + stack.pop()); // タスク2が出力される
// キューとして利用する場合 (FIFO: 先入れ先出し)
Deque
queue.offer(“依頼A”);
queue.offer(“依頼B”);
System.out.println(“キューから取り出し: ” + queue.poll()); // 依頼Aが出力される
// 応用: 先頭と末尾の両方にアクセス可能
queue.addFirst(“最優先”);
queue.addLast(“最後尾”);
System.out.println(“現在のキューの状態: ” + queue);
}
}
応用・注意点
ArrayDequeはスレッドセーフではありません。マルチスレッド環境で複数のスレッドから同時にアクセスを行う場合は、java.util.concurrent.ConcurrentLinkedDequeを使用するか、外部で同期化を行う必要があります。
また、Java 21から導入された「Sequenced Collections」インターフェースにより、DequeはgetFirst()やgetLast()といったメソッドがより直感的に扱えるようになりました。最新のJava環境であれば、これらのメソッドを活用することで、コードの可読性が格段に向上します。実務では「とりあえずArrayList」とするのではなく、その用途が「単なるリスト保持」なのか「順序付きの加工」なのかを見極め、ArrayDequeを積極的に採用してみてください。

コメント