タイトル: List
SEOタイトル: Java List インターフェース完全ガイド - ArrayList / LinkedList / List.of / immutable
| この記事の要点 |
|
List とは
List は java.util.List インターフェースで、Java Collections Framework の代表格です。要素の順序を保ち、重複を許すコレクションを抽象化しており、複数の具体実装が用意されています。
import java.util.*;
// インターフェース宣言、ArrayList 実装
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Alice"); // 重複 OK
System.out.println(names); // [Alice, Bob, Alice]
System.out.println(names.size()); // 3
System.out.println(names.get(0)); // Alice
names.set(0, "Carol"); // 上書き
names.remove(1); // インデックス1削除
主要メソッド
| メソッド | 役割 |
|---|---|
add(e) | 末尾に追加 |
add(i, e) | 位置 i に挿入 |
get(i) | 位置 i の要素取得 |
set(i, e) | 位置 i を e で置換 |
remove(i) / remove(Object) | 削除 |
indexOf(e) / lastIndexOf(e) | 位置検索 |
contains(e) | 含まれるか |
subList(from, to) | 部分リスト(ビュー) |
size() | 要素数 |
isEmpty() | 空か |
iterator() / listIterator() | 反復 |
toArray() | 配列化 |
stream() | Stream 化 |
sort(Comparator) Java 8+ | ソート |
主要実装の比較
| 実装 | 内部構造 | get(i) | add(末尾) | add(0) | remove(i) | 用途 |
|---|---|---|---|---|---|---|
| ArrayList | 動的配列 | O(1) | 償却 O(1) | O(n) | O(n) | 汎用、デフォルト選択 |
| LinkedList | 双方向連結リスト | O(n) | O(1) | O(1) | O(n) | 先頭/末尾の追加削除多発(Deque 推奨) |
| Vector | 動的配列 + synchronized | O(1) | O(1) | O(n) | O(n) | レガシー(非推奨) |
| CopyOnWriteArrayList | 配列、書込時にコピー | O(1) | O(n) | O(n) | O(n) | 読み多・書き少の並行処理 |
ArrayList の使い方
import java.util.*;
// 初期容量指定(要素数が予測できる場合は推奨)
List<Integer> list = new ArrayList<>(1000);
for (int i = 0; i < 1000; i++) {
list.add(i);
}
// 走査(拡張 for)
for (Integer n : list) {
System.out.println(n);
}
// イテレータ(途中削除可能)
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
if (it.next() % 2 == 0) it.remove();
}
// Stream(関数型)
List<Integer> evens = list.stream()
.filter(n -> n % 2 == 0)
.toList(); // Java 16+ で immutable List 返す
LinkedList と ArrayDeque
「先頭/末尾の追加削除が多いなら LinkedList」と語られがちですが、実務では ArrayDeque の方が速いケースが多いです:
// LinkedList は List + Deque を兼ねる
LinkedList<String> ll = new LinkedList<>();
ll.addFirst("A");
ll.addLast("B");
ll.removeFirst();
// ArrayDeque は Deque のみ。List ではない。
// が、ノード allocate が無いため LinkedList より速い
Deque<String> dq = new ArrayDeque<>();
dq.push("A"); // 先頭追加
dq.offer("B"); // 末尾追加
dq.poll(); // 先頭取り出し
// 結論: List インターフェースが必要なら ArrayList。
// FIFO/LIFO/Deque 用途なら ArrayDeque。
不変リスト (Immutable List)
// Java 9+ : List.of() で不変リスト
List<String> imm = List.of("A", "B", "C");
imm.add("D"); // ❌ UnsupportedOperationException
// 旧来の方法
List<String> imm2 = Collections.unmodifiableList(
new ArrayList<>(Arrays.asList("A", "B", "C"))
);
// Arrays.asList は ★ サイズ固定 ★ で完全不変ではない
List<String> fixed = Arrays.asList("A", "B", "C");
fixed.set(0, "Z"); // ✅ OK(要素変更は可)
fixed.add("D"); // ❌ UnsupportedOperationException
// Java 10+ : copyOf で防御的コピーの不変リスト
List<String> copy = List.copyOf(mutableList);
3 種類の "不変っぽい" リスト比較
| 方法 | add / remove | set | 元データの変更 |
|---|---|---|---|
List.of() | 不可 | 不可 | 影響なし(完全不変) |
Arrays.asList() | 不可 | 可 | 配列を共有(変更が反映) |
Collections.unmodifiableList() | 不可 | 不可 | 元リストの変更が反映(ビュー) |
並行処理での List
// ❌ ArrayList は非スレッドセーフ。複数スレッド書込みでデータ破損
List<String> unsafe = new ArrayList<>();
// 方法1: synchronized ラッパー
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
// 方法2: CopyOnWriteArrayList(読み多・書き少向け)
List<String> cow = new CopyOnWriteArrayList<>();
cow.add("A"); // 書き込み毎に内部配列をコピー(O(n))
cow.forEach(System.out::println); // 書込みと並行しても安全
// 方法3: 不変リスト(最も安全)
List<String> imm = List.copyOf(source);
よくある落とし穴
| 症状 | 原因 | 対処 |
|---|---|---|
ConcurrentModificationException | iterate 中に add/remove | Iterator#remove() / CopyOnWrite |
UnsupportedOperationException on add | 不変リスト / Arrays.asList | new ArrayList<>(asList(…)) |
| 大量データで性能劣化 | 初期容量未指定で再 allocate 多発 | new ArrayList<>(N) |
List.of(null) でエラー | List.of は null 禁止 | Arrays.asList / 自前リスト |
| subList 後に元リストを変更したら fail | subList は "ビュー" | 独立コピーが必要なら new ArrayList<>(sub) |
FAQ
Q: ArrayList と LinkedList、どちらを使えば良い?
A: 99% は ArrayList。理由は キャッシュ効率(配列の連続メモリ)と get(i) が O(1) であること。LinkedList が勝るのは「先頭/末尾の頻繁な追加削除」だが、その用途は ArrayDeque の方が更に高速。
Q: List.of の最大要素数は?
A: Java 9 では of(e1)〜of(e1...e10) の固定オーバーロードと、可変長 of(E... elements) があり実質無制限。10 個まではオートボクシング無しの最適化があります。
Q: スレッドセーフな List が欲しい
A: 読み多なら CopyOnWriteArrayList、読み書き均等なら Collections.synchronizedList、可能なら不変リスト(List.copyOf)が最も安全。