概要
業務コードでは List、Map、Set のどれかを選んでデータを保持する場面が日常的に発生します。ArrayList と LinkedList のどちらを使うか、HashMap と TreeMap の違いは何か、初期化に List.of を使って良いのかなど、基本でありながら実務で判断を迫られるポイントは意外と多くあります。この記事では、各コレクションの特性と選定基準を整理したうえで、不変コレクション(List.of / Map.of)と Collections.unmodifiableList の違い、Java 21 で追加された SequencedCollection の意味合いまでを、動くコードとともに解説します。外部ライブラリなしで扱える範囲に絞り、現場でそのまま使える初期化パターンとよくある落とし穴を押さえます。
使いどころ
マスタデータの一覧を List.of で不変リストとして保持し、画面表示やバリデーションの参照元にする
商品コードと商品名の対応表を Map.of で定義し、CSV 取込時の名称変換に使う
重複チェックが必要な入力値(メールアドレス等)を HashSet に投入し、重複検知と排除を同時に行う
コード例
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());
}
}Version Coverage
List.of / Map.of / Set.of で簡潔に不変コレクションを生成できる。List.copyOf で安全なコピーも可能。var で型推論も利用可。
// Java 17: List.of で完全な不変リストを簡潔に生成
List<String> immutable = List.of("Java", "Python", "Go");
// immutable.add("Kotlin") → UnsupportedOperationException
// 変更可能にしたい場合は ArrayList でラップ
var mutable = new ArrayList<>(immutable);Library Comparison
注意点
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 はランダムアクセスが O(n) で遅く、メモリ消費も大きいため、先頭への頻繁な挿入・削除が必要な場面以外では選ぶ理由が薄いです。
新しいコードでは List.of を推奨します。unmodifiableList は元リストの変更が透過するため、完全な不変性が保証されません。既存コードとの互換で元リストを持つ場合は List.copyOf を検討してください。
Map.ofEntries(Map.entry("key1", val1), Map.entry("key2", val2), ...) を使います。可変長引数で件数の上限がなく、不変マップを返します。