概要

業務コードでは List、Map、Set のどれかを選んでデータを保持する場面が日常的に発生します。ArrayList と LinkedList のどちらを使うか、HashMap と TreeMap の違いは何か、初期化に List.of を使って良いのかなど、基本でありながら実務で判断を迫られるポイントは意外と多くあります。この記事では、各コレクションの特性と選定基準を整理したうえで、不変コレクション(List.of / Map.of)と Collections.unmodifiableList の違い、Java 21 で追加された SequencedCollection の意味合いまでを、動くコードとともに解説します。外部ライブラリなしで扱える範囲に絞り、現場でそのまま使える初期化パターンとよくある落とし穴を押さえます。

使いどころ

マスタデータの一覧を List.of で不変リストとして保持し、画面表示やバリデーションの参照元にする

商品コードと商品名の対応表を Map.of で定義し、CSV 取込時の名称変換に使う

重複チェックが必要な入力値(メールアドレス等)を HashSet に投入し、重複検知と排除を同時に行う

コード例

CollectionBasics.java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class CollectionBasics {

    public static void main(String[] args) {

        // List.of() で不変リストを簡潔に作成(Java 9+)
        List<String> languages = List.of("Java", "Python", "Go");
        System.out.println("不変リスト: " + languages);

        // 変更可能にするには ArrayList でラップ
        var mutableList = new ArrayList<>(languages);
        mutableList.add("Kotlin");
        System.out.println("変更可能リスト: " + mutableList);

        // Map.of() で不変マップを簡潔に作成(最大10エントリー)
        Map<String, Integer> priceMap = Map.of(
                "apple", 100, "banana", 150, "cherry", 200);
        System.out.println("apple の価格: " + priceMap.get("apple"));

        // HashMap で変更可能なマップを作成
        var mutableMap = new HashMap<>(priceMap);
        mutableMap.put("grape", 250);

        // getOrDefault でキー不在時のデフォルト値を指定
        int price = mutableMap.getOrDefault("melon", 0);
        System.out.println("melon の価格(デフォルト0): " + price);

        // TreeMap でキー昇順に自動ソート
        var sortedMap = new TreeMap<>(mutableMap);
        System.out.println("TreeMap(昇順): " + sortedMap);

        Set<String> tags = new HashSet<>();
        tags.add("Java");
        tags.add("Stream");
        tags.add("Java"); // 重複は無視される
        System.out.println("Set size: " + tags.size()); // 2

        // Set.of() で不変セット
        Set<String> immutableTags = Set.of("A", "B", "C");
        System.out.println("不変セット: " + immutableTags);

        Map<String, Integer> largeMap = Map.ofEntries(
                Map.entry("a", 1), Map.entry("b", 2),
                Map.entry("c", 3), Map.entry("d", 4),
                Map.entry("e", 5), Map.entry("f", 6),
                Map.entry("g", 7), Map.entry("h", 8),
                Map.entry("i", 9), Map.entry("j", 10),
                Map.entry("k", 11));
        System.out.println("Map.ofEntries size: " + largeMap.size());
    }
}

Java 8 / 17 / 21 の完全なサンプルコードは GitHub リポジトリ で確認できます。

Version Coverage

List.of / Map.of / Set.of で簡潔に不変コレクションを生成できる。List.copyOf で安全なコピーも可能。var で型推論も利用可。

Java 17
// Java 17: List.of で完全な不変リストを簡潔に生成
List<String> immutable = List.of("Java", "Python", "Go");
// immutable.add("Kotlin") → UnsupportedOperationException
// 変更可能にしたい場合は ArrayList でラップ
var mutable = new ArrayList<>(immutable);

Library Comparison

標準 API(java.util)List / Map / Set の基本操作と不変コレクションの生成で事足りる場面。依存なしで済む。Multimap や BiMap のような特殊構造が必要な場合は自前実装が冗長になる。
Guava(ImmutableList / ImmutableMap)Java 8 環境で不変コレクションを安全に扱いたいとき。Multimap や BiMap が必要な場合。Java 9 以降は List.of / Map.of で大半がカバーできるため、新規プロジェクトでは導入理由が薄い。
Eclipse Collectionsプリミティブ特化コレクション(IntList 等)でメモリ効率を追求する大量データ処理。API 体系が独自で学習コストが高い。標準 API との混在はかえってコードの統一感を損なう。

注意点

List.of() や Map.of() が返す不変コレクションに add/put を呼ぶと UnsupportedOperationException になる。変更が必要な場合は new ArrayList<>(List.of(...)) でラップする

Collections.unmodifiableList は元のリストへの参照を保持するため、元リストが変更されると不変リスト側にも影響する。完全に独立したコピーが必要なら List.copyOf を使う

Map.of は最大10エントリーまでしか受け付けない。11件以上は Map.ofEntries + Map.entry で初期化する

HashMap のキーに可変オブジェクトを使うと、状態変更後に get で見つからなくなる。キーには String や Integer など不変型を使うのが原則

TreeMap はキーの自然順序(Comparable)で並ぶが、null キーを入れると NullPointerException になる。null キーが必要な場面は HashMap を使う

FAQ

ArrayList と LinkedList はどう使い分ければよいですか。

ほとんどの業務コードでは ArrayList で十分です。LinkedList はランダムアクセスが O(n) で遅く、メモリ消費も大きいため、先頭への頻繁な挿入・削除が必要な場面以外では選ぶ理由が薄いです。

List.of と Collections.unmodifiableList のどちらを使うべきですか。

新しいコードでは List.of を推奨します。unmodifiableList は元リストの変更が透過するため、完全な不変性が保証されません。既存コードとの互換で元リストを持つ場合は List.copyOf を検討してください。

Map.of で11件以上のエントリーを初期化するにはどうすればよいですか。

Map.ofEntries(Map.entry("key1", val1), Map.entry("key2", val2), ...) を使います。可変長引数で件数の上限がなく、不変マップを返します。

関連書籍

この記事のテーマをさらに深く学びたい方へ。

※ Amazon アソシエイトリンクを含みます