タイトル: ArrayListクラス
SEOタイトル: Java ArrayList完全ガイド(容量/API/List.of/スレッドセーフ/性能)
| この記事の要点 |
|
ArrayList とは
java.util.ArrayList は内部に配列を持つ可変長 List 実装です。インデックスアクセスは O(1)、末尾への追加は償却 O(1)、中間挿入/削除は O(n)。Java で最も多用されるコレクションです。
生成と初期化
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
// 空の ArrayList
List a1 = new ArrayList<>();
// 初期容量指定(大量追加が分かっているとき必須)
List a2 = new ArrayList<>(1000);
// 既存コレクションから
List a3 = new ArrayList<>(Arrays.asList("a", "b", "c"));
List a4 = new ArrayList<>(List.of("a", "b", "c"));
// 配列から
String[] arr = {"x", "y", "z"};
List a5 = new ArrayList<>(Arrays.asList(arr));
主要メソッド一覧
| API | 計算量 | 説明 |
|---|---|---|
add(e) | 償却 O(1) | 末尾追加 |
add(i, e) | O(n) | 位置 i に挿入(以降ずらす) |
get(i) | O(1) | 位置 i 取得 |
set(i, e) | O(1) | 位置 i を上書き |
remove(i) | O(n) | 位置 i 削除(以降詰める) |
remove(Object) | O(n) | 最初に一致する要素削除 |
contains(e) | O(n) | 線形探索 |
indexOf(e) | O(n) | 線形探索 |
size() | O(1) | 要素数 |
isEmpty() | O(1) | 空か |
clear() | O(n) | 全削除(参照を null に) |
subList(from, to) | O(1) | ビュー(元と連動) |
toArray() | O(n) | 配列化 |
List list = new ArrayList<>();
list.add("Alice");
list.add("Bob");
list.add(0, "Header"); // 先頭に挿入: [Header, Alice, Bob]
String x = list.get(1); // Alice
list.set(1, "Anna"); // [Header, Anna, Bob]
list.remove("Bob"); // [Header, Anna]
boolean hasA = list.contains("Anna"); // true
int idx = list.indexOf("Header"); // 0
list.forEach(System.out::println);
list.removeIf(s -> s.startsWith("H")); // [Anna]
List.of / Arrays.asList / new ArrayList の違い
| サイズ | 要素変更 | null 許容 | 用途 | |
|---|---|---|---|---|
List.of("a","b") | 固定 | 不可(UnsupportedOperationException) | 不可 | ★ 不変リスト |
Arrays.asList("a","b") | 固定長 | 可(set のみ) | 可 | 配列のリストビュー |
new ArrayList<>(List.of(...)) | 可変 | 可 | 可 | ★ 通常用途 |
// ❌ Arrays.asList の add でエラー
List a = Arrays.asList("a", "b");
a.add("c"); // UnsupportedOperationException
// ❌ List.of の set でエラー
List b = List.of("a", "b");
b.set(0, "x"); // UnsupportedOperationException
// ✅ 変更したいなら ArrayList でラップ
List c = new ArrayList<>(List.of("a", "b"));
c.add("c"); // OK
不変化と Read-Only ビュー
List mutable = new ArrayList<>(List.of("a", "b"));
// Read-only ビュー(元を変えるとビューも変わる)
List ro = Collections.unmodifiableList(mutable);
ro.add("c"); // UnsupportedOperationException
mutable.add("c"); // OK → ro でも "c" が見える
// 完全コピーして不変化
List immutable = List.copyOf(mutable);
mutable.add("d");
// → immutable は変わらない
内部実装と容量
内部に Object[] を持ち、容量が足りなくなったら1.5 倍に拡張します(newCapacity = oldCapacity + (oldCapacity >> 1))。デフォルト初期容量は 10。
// ❌ 100 万要素を追加するなら容量を最初に確保
List bad = new ArrayList<>();
for (int i = 0; i < 1_000_000; i++) bad.add(i);
// → 内部で何回もリサイズ・コピー発生
// ✅ 容量指定で高速化
List good = new ArrayList<>(1_000_000);
for (int i = 0; i < 1_000_000; i++) good.add(i);
// trimToSize() で余剰容量を解放
((ArrayList) good).trimToSize();
スレッドセーフ
ArrayList はスレッドセーフではありません。複数スレッドから書き込むと ConcurrentModificationException や ArrayIndexOutOfBoundsException が発生します。
// 選択肢1: Collections.synchronizedList
List s = Collections.synchronizedList(new ArrayList<>());
synchronized (s) { // iterate は要外部同期
for (String x : s) System.out.println(x);
}
// 選択肢2: CopyOnWriteArrayList(読み多書き少)
List cow = new CopyOnWriteArrayList<>();
// 書き込みごとに配列を丸ごとコピー → 重いが iterate は安全
// 選択肢3: スレッドごとに別リスト → 最後に集約(推奨)
List> partial = ...; // 各スレッド独立
List merged = partial.stream()
.flatMap(List::stream)
.toList();
性能ヒント
- 大量の末尾追加 → ArrayList(初期容量指定)
- 大量の先頭/中間挿入 →
ArrayDequeやLinkedList - 頻繁な contains 検索 →
HashSetを併用 - iterate 中の削除 →
Iterator.remove()またはremoveIf - 巨大データのメモリ削減 → プリミティブ特化型 (Eclipse Collections, fastutil)
FAQ
Q: list.remove(1) はインデックス削除?要素削除?
A: List だとインデックス削除が優先されます。要素 1 を消したいなら list.remove(Integer.valueOf(1))。
Q: subList は新しいリスト?
A: ビューです。元を変更するとビューに反映されます。独立したコピーがほしければ new ArrayList<>(list.subList(0, 10))。
Q: ArrayList と Vector の違いは?
A: Vector は古いスレッドセーフ実装。現在では synchronizedList や CopyOnWriteArrayList に置き換えるべきです。