Collections.swap / replaceAll — リスト操作の簡潔化
リスト要素の入れ替えや一括置換を「短く安全に」書けるのが Collections.swap と Collections.replaceAll。拡張 for で直接編集して例外…を避けつつ、読みやすいコードにできます。初心者向けに、違い・使い方・落とし穴・テンプレートをまとめます。
基本の考え方と違い
- Collections.swap(List, i, j): 指定インデックスどうしの要素を入れ替える(O(1))。
- Collections.replaceAll(List, oldVal, newVal): リスト内の「equals 一致」する要素をすべて新値に置換(in-place)。
- List.replaceAll(UnaryOperator): 各要素を関数で「変換」して置き換える。
Collections.replaceAllとは用途が違う(値一致の一括置換 vs 関数による一括変換)。
すぐ試せる基本例
swap(入れ替え)
import java.util.*;
List<String> list = new ArrayList<>(List.of("A", "B", "C", "D"));
Collections.swap(list, 1, 3); // index1=1, index2=3
System.out.println(list); // [A, D, C, B]
JavareplaceAll(値一致で一括置換)
List<String> list = new ArrayList<>(List.of("X", "A", "X", "B"));
Collections.replaceAll(list, "X", "Y");
System.out.println(list); // [Y, A, Y, B]
JavaList.replaceAll(関数で一括変換)
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
list.replaceAll(String::toUpperCase);
System.out.println(list); // [A, B, C]
Java例題で理解する
例題1: トランプのシャッフル中に特定カードの位置調整(swap)
List<String> deck = new ArrayList<>(List.of("A", "K", "Q", "J"));
int i = deck.indexOf("K");
int j = deck.indexOf("Q");
if (i >= 0 && j >= 0) Collections.swap(deck, i, j);
System.out.println(deck); // [A, Q, K, J]
Java- ねらい: インデックスが分かれば、入れ替えは一行。
例題2: 欠番タグの一括置換(replaceAll)
List<String> tags = new ArrayList<>(List.of("draft", "NA", "published", "NA"));
Collections.replaceAll(tags, "NA", "unknown");
System.out.println(tags); // [draft, unknown, published, unknown]
Java- ねらい: equals 一致する「特定値」だけを安全に置換。
例題3: スコアを「補正関数」で一括更新(List.replaceAll)
List<Integer> scores = new ArrayList<>(List.of(70, 80, 90));
scores.replaceAll(x -> Math.min(100, x + 5));
System.out.println(scores); // [75, 85, 95]
Java- ねらい: 値一致ではなく、ルールに沿った全体更新。
よくある落とし穴と回避策
- replaceAll の取り違え:
- 迷いどころ:
Collections.replaceAll(list, old, new)は「値一致」で置換。String.replaceAllは正規表現で「文字列」を置換、別物。 - 回避: リストなら
Collections.replaceAll、文字列ならString.replace/replaceAll(regex)を使い分ける。
- 迷いどころ:
- インデックス範囲外(swap):
- 原因: -1(見つからない)や範囲外のインデックスを渡す。
- 回避:
indexOfの結果をチェックしてからswap。境界検証は必須。
- 不変/非対応リスト:
- 原因:
List.of(...)やArrays.asList(...)は変更不可または制限あり。 - 回避: 変更前に
new ArrayList<>(...)で可変リストへ。
- 原因:
- subList のビュー問題:
- 原因:
subListは元のリストのビュー。編集は元にも反映、外側で構造変更すると例外の可能性。 - 回避: ビューではなくコピーを編集したいなら
new ArrayList<>(subList)。
- 原因:
- equals の定義に依存(replaceAll):
- 原因: 独自型の置換で
equalsが未定義 or 不適切。 - 回避: キーにするフィールドで
equals/hashCodeを正しく実装。
- 原因: 独自型の置換で
実用レシピ
位置指定で入れ替え(安全チェック付き)
void safeSwap(List<?> list, int i, int j) {
if (i < 0 || j < 0 || i >= list.size() || j >= list.size()) return;
Collections.swap(list, i, j);
}
Java値一致の一括置換(null セーフ)
void replaceAllNullSafe(List<String> list, String oldVal, String newVal) {
for (ListIterator<String> it = list.listIterator(); it.hasNext(); ) {
String v = it.next();
if (Objects.equals(v, oldVal)) it.set(newVal);
}
}
Java部分範囲だけ操作(subList と組み合わせ)
List<String> list = new ArrayList<>(List.of("A","B","C","D","E"));
List<String> mid = list.subList(1, 4); // [B,C,D]
Collections.swap(mid, 0, 2); // 範囲内入れ替え → 元にも反映
System.out.println(list); // [A, D, C, B, E]
Javaテンプレート集(そのまま使える形)
- swap: インデックスで入れ替え
Collections.swap(list, i, j);
Java- replaceAll: 値一致で一括置換
Collections.replaceAll(list, oldValue, newValue);
Java- List.replaceAll: 関数で一括変換
list.replaceAll(op); // op: UnaryOperator<T>(例: x -> x.toUpperCase())
Java- 可変リスト化(変更前の定型)
list = new ArrayList<>(list);
Javaまとめ
Collections.swapはインデックス指定での入れ替え、Collections.replaceAllは equals による値一致の一括置換。- ルールで全体更新したいなら
List.replaceAll(UnaryOperator)。 - 不変リスト、subList のビュー、equals 依存、インデックス境界などの落とし穴を避ければ、短く安全に「位置操作・置換」を書けます。
