この記事の要点
• List はインターフェース、ArrayList / LinkedList は実装クラス
• ArrayList はランダムアクセスが速い、LinkedList は挿入・削除が速い
• `List.of()` で不変リストを簡潔に作成できる(Java 9以降)
List インターフェースとは
前回(#15)で学んだ ArrayList は、実は List というインターフェースを実装したクラスの1つです。
インターフェースとは「こういうメソッドを持っていますよ」という契約のようなものです。List インターフェースは「順序を持つコレクション」の共通操作(add / get / remove など)を定義しています。
| 用語 | 説明 |
|---|---|
| List | インターフェース(契約)。順序を持つコレクションの共通仕様 |
| ArrayList | List を実装したクラス。内部で配列を使用 |
| LinkedList | List を実装したクラス。内部で双方向リンクリストを使用 |
ポイント: コードでは変数の型を `List` にしておき、生成時に `ArrayList` か `LinkedList` かを選ぶのが一般的です。これをプログラミング・トゥ・インターフェースと呼びます。
List 型で宣言する利点
// Java 21
import java.util.List;
import java.util.ArrayList;
// 推奨: 型は List、実装は ArrayList
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
なぜ ArrayList<String> names ではなく List<String> names とするのか?
| メリット | 説明 |
|---|---|
| 柔軟性 | 後で LinkedList に変更しても、利用側のコードは変わらない |
| テスト容易性 | モック(テスト用ダミー)に差し替えやすい |
| 設計の明確化 | 「順序付きリストであればOK」という意図が伝わる |
ArrayList と LinkedList の違い
どちらも List インターフェースを実装していますが、内部構造が異なります。
| 項目 | ArrayList | LinkedList |
|---|---|---|
| 内部構造 | 可変長配列(連続メモリ) | 双方向リンクリスト(ノードのつながり) |
| ランダムアクセス(get) | O(1)(超高速) | O(n)(遅い) |
| 末尾追加(add) | O(1)(平均) | O(1) |
| 先頭・途中挿入 | O(n)(要素をずらす) | O(1)(ポインタ変更のみ) |
| 途中削除 | O(n)(要素を詰める) | O(1)(ポインタ変更のみ) |
| メモリ効率 | 良い | やや悪い(各ノードにポインタが必要) |
| 用途 | 一般的なリスト操作全般(デフォルト) | 頻繁な挿入・削除がある場合 |
注意: 特に理由がなければArrayListを使ってください。LinkedList は挿入・削除が多い特殊なケースでのみ有利です。
パフォーマンス比較の実例
以下の操作を10万回繰り返した場合の計算量イメージです。
| 操作 | ArrayList | LinkedList | 速いのは |
|---|---|---|---|
get(i) で全要素読み取り | O(n) | O(n²) | ArrayList |
add(0, element) で先頭挿入 | O(n²) | O(n) | LinkedList |
remove(0) で先頭削除 | O(n²) | O(n) | LinkedList |
add(element) で末尾追加 | O(n) | O(n) | ほぼ同じ |
補足: LinkedList は Queue(キュー)や Deque(両端キュー)として使うために設計されています。スタックやキュー操作が必要な場合に検討してください。
LinkedList の基本操作
基本的なメソッドは ArrayList と同じですが、LinkedList 特有のメソッドもあります。
// Java 21
import java.util.LinkedList;
LinkedList<String> queue = new LinkedList<>();
// 末尾に追加(ArrayList と同じ)
queue.add("Alice");
queue.add("Bob");
// 先頭に追加(LinkedList 特有)
queue.addFirst("Charlie"); // ["Charlie", "Alice", "Bob"]
// 先頭を削除(LinkedList 特有)
String first = queue.removeFirst(); // "Charlie"
// 末尾を削除(LinkedList 特有)
String last = queue.removeLast(); // "Bob"
| LinkedList 特有メソッド | 説明 |
|---|---|
addFirst(E) | 先頭に追加 |
addLast(E) | 末尾に追加(add と同じ) |
removeFirst() | 先頭を削除して返す |
removeLast() | 末尾を削除して返す |
getFirst() | 先頭を取得(削除しない) |
getLast() | 末尾を取得(削除しない) |
ポイント: これらのメソッドは Queue / Deque インターフェースからも提供されます。LinkedList は Queue と Deque の両方を実装しています。
List の主要メソッド(共通)
ArrayList と LinkedList の両方で使える主要メソッドです。
| メソッド | 説明 | 戻り値 |
|---|---|---|
add(E element) | 末尾に追加 | boolean |
add(int index, E element) | 指定位置に挿入 | void |
get(int index) | 指定位置の要素を取得 | E |
set(int index, E element) | 指定位置を上書き | E |
remove(int index) | 指定位置を削除 | E |
remove(Object o) | 指定値を削除(最初の1件) | boolean |
size() | 要素数 | int |
isEmpty() | 空かどうか | boolean |
contains(Object o) | 要素が含まれるか | boolean |
clear() | 全削除 | void |
不変リスト(List.of)
不変リストは、一度作ったら追加・削除・変更ができないリストです。Java 9 以降、List.of() で簡潔に作成できます。
// Java 21
import java.util.List;
// 不変リストを作成
List<String> colors = List.of("Red", "Green", "Blue");
System.out.println(colors.get(0)); // Red
System.out.println(colors.size()); // 3
// 変更しようとするとエラー
// colors.add("Yellow"); // UnsupportedOperationException
| 特徴 | 説明 |
|---|---|
| 不変 | add / remove / set が使えない(実行時エラー) |
| 簡潔 | new ArrayList<>(Arrays.asList(...)) より短い |
| null 禁止 | List.of(null) は NullPointerException |
| 軽量 | 内部構造が最適化されている |
注意: 不変リストに変更操作(add / remove / set)を実行すると `UnsupportedOperationException` が発生します。
実践例: タスク管理(先頭挿入が頻繁)
先頭への挿入が頻繁な場合、LinkedList が有利です。
// Java 21
import java.util.LinkedList;
import java.util.List;
public class Main {
public static void main(String[] args) {
LinkedList<String> tasks = new LinkedList<>();
// タスクを追加(先頭に緊急タスクを挿入する運用)
tasks.add("メールチェック");
tasks.add("資料作成");
tasks.addFirst("緊急:バグ修正"); // 先頭に挿入
System.out.println("現在のタスク:");
for (String task : tasks) {
System.out.println("- " + task);
}
// 先頭から順次処理
while (!tasks.isEmpty()) {
String current = tasks.removeFirst();
System.out.println("処理中: " + current);
}
}
}
出力:
現在のタスク:
- 緊急:バグ修正
- メールチェック
- 資料作成
処理中: 緊急:バグ修正
処理中: メールチェック
処理中: 資料作成
選択のガイドライン
| 状況 | 推奨 | 理由 |
|---|---|---|
| 一般的なリスト操作 | ArrayList | ランダムアクセスが速く、メモリ効率も良い |
| インデックスでの頻繁な読み取り | ArrayList | get(i) が O(1) |
| 先頭・途中への頻繁な挿入 | LinkedList | ポインタ変更のみで O(1) |
| キュー(FIFO) | LinkedList | addLast() / removeFirst() が効率的 |
| スタック(LIFO) | LinkedList または ArrayDeque | addFirst() / removeFirst() |
| 読み取り専用 | List.of() | 不変で安全、軽量 |
ポイント: 迷ったらArrayListを使いましょう。ほとんどのケースで最速です。LinkedList は「先頭挿入が超頻繁」「キュー実装」など明確な理由がある場合のみ選択してください。
まとめ
| 概念 | 説明 |
|---|---|
| List | 順序付きコレクションのインターフェース |
| ArrayList | 配列ベースの実装。ランダムアクセス O(1)、一般用途に最適 |
| LinkedList | リンクリストベースの実装。先頭挿入・削除 O(1)、キュー向き |
| プログラミング・トゥ・インターフェース | List<String> で宣言し、実装を後で変更可能にする |
| List.of() | 不変リストを簡潔に作成(Java 9以降) |
| パフォーマンス | 一般には ArrayList、先頭操作が多いなら LinkedList |
次のステップ
Java入門 #17 - HashMap(キーと値のペア) で、キーと値を関連付けるマップ(辞書)を学びます。
参考リソース
- Oracle List API ドキュメント — List インターフェース公式リファレンス
- Oracle ArrayList API
- Oracle LinkedList API
- Java Collections Framework — 公式チュートリアル