Java 逆引き集 | Collections.swap / replaceAll — リスト操作の簡潔化

Java Java
スポンサーリンク

Collections.swap / replaceAll — リスト操作の簡潔化

リスト要素の入れ替えや一括置換を「短く安全に」書けるのが Collections.swapCollections.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]
Java

replaceAll(値一致で一括置換)

List<String> list = new ArrayList<>(List.of("X", "A", "X", "B"));
Collections.replaceAll(list, "X", "Y");
System.out.println(list); // [Y, A, Y, B]
Java

List.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 依存、インデックス境界などの落とし穴を避ければ、短く安全に「位置操作・置換」を書けます。
タイトルとURLをコピーしました