【Java学習|実務向け】Javaエンジニアが押さえるべき「コレクション状態」の定義と使い分け

1. 導入:なぜコレクションの「状態」を意識すべきか

Java開発において、ListやMapを扱う際に「このコレクションは後から変更して良いのか?」という設計意図が不明確だと、意図しない副作用(バグ)や並行処理時の不整合が発生します。特にAPI設計やドメインモデルにおいて、状態の定義(Modifiable, Unmodifiable, Immutable)を厳密に使い分けることは、堅牢なコードを書くための第一歩です。本記事では、これらの違いと実務での使い分けを解説します。

2. 基礎知識:3つの状態の定義

Javaのコレクションを理解する上で、以下の3つの区分を明確にしましょう。

Modifiable(可変): 要素の追加・削除・置換が可能な状態。デフォルトのArrayListやHashMapなどが該当します。
Unmodifiable(変更不可): コレクションへの変更操作(addやremove)を呼び出すと、UnsupportedOperationExceptionが発生する状態。ただし、内部の元のコレクションが変更されれば、その影響を受けます。
Immutable(不変): 生成後に状態が一切変化しないこと。外部から変更されることがなく、スレッドセーフであるため、現代のJava開発では強く推奨されます。

3. 実装と解決策

Java 9以降、標準APIでこれらのコレクションを生成する方法が整備されました。

List.of() / Set.of() / Map.of(): これらはImmutableなコレクションを生成します。要素の追加・削除はもちろん、nullの保持も許可されません。
Collections.unmodifiableList(): 既存の可変コレクションをラップして「変更不可」にします。元のコレクションが変更されると、こちらにも影響が出る点に注意が必要です。

実務では、メソッドの引数や戻り値として「変更してほしくない」場合は、可能な限りImmutableなコレクションを返す設計を心がけましょう。

4. サンプルプログラム

以下のコードで、各状態の挙動と注意点を確認してください。

import java.util.;

public class CollectionExample {
    public static void main(String[] args) {
        // 1. Modifiable: 自由に操作可能
        List<String> mutableList = new ArrayList<>(Arrays.asList("A", "B"));
        mutableList.add("C");

        // 2. Unmodifiable (View): 元のリストが変更されると影響を受ける
        List<String> unmodifiableView = Collections.unmodifiableList(mutableList);
        System.out.println("View: " + unmodifiableView); // [A, B, C]
        mutableList.add("D"); // 元を変更すると...
        System.out.println("View after source change: " + unmodifiableView); // [A, B, C, D]

        // 3. Immutable: 生成後に一切変化しない(推奨)
        List<String> immutableList = List.of("A", "B", "C");
        try {
            immutableList.add("D"); // ここで UnsupportedOperationException が発生
        } catch (UnsupportedOperationException e) {
            System.out.println("Immutableは変更不可です");
        }
    }
}

5. 応用・注意点:現場で陥りやすい罠

nullの扱いに注意: List.of()などのImmutableなファクトリメソッドは、nullを格納しようとするとNullPointerExceptionを投げます。DBの検索結果など、nullが含まれる可能性がある場合は、Stream APIのfilter(Objects::nonNull)などを活用して除去してから生成してください。

Sequenced Collectionsの活用: Java 21からはLinkedHashSetやArrayListがSequencedCollectionインターフェースを実装しました。これにより、先頭や末尾へのアクセスが容易になっています。これらも「順序を保持する」という特性を理解した上で、Immutableなコピーを返す設計にすると、デバッグが非常に楽になります。

まとめ:
・外部に公開するデータは可能な限りImmutableにする。
・「読み取り専用」であることを明示したい場合はUnmodifiableViewを使う。
・スレッドセーフを意識するなら、迷わずImmutableを選択する。

この指針を持つだけで、コードの品質と保守性は一段階向上します。ぜひプロジェクトで意識してみてください。

コメント

タイトルとURLをコピーしました