MultiMap パターン(Map<K, List<V>>) — 1対多管理
Java の標準ライブラリには「MultiMap」というクラスはありません。
しかし「1つのキーに複数の値を紐付けたい」場面はよくあります。そこでよく使われるのが Map<K, List<V>> というパターンです。初心者向けに、コード例とテンプレートを交えて解説します。
基本アイデア
- 通常の Map: キー1つに値1つ。
- MultiMap パターン: キー1つに「値のリスト」を持たせる。
- 用途:
- 学生名簿(クラス → 学生一覧)
- 商品カテゴリ(カテゴリ → 商品一覧)
- イベント管理(日付 → イベント一覧)
👉 Map<K, List<V>> を使えば「1対多」を自然に表現できる。
基本コード例
1. 初期化と追加
import java.util.*;
public class MultiMapDemo {
public static void main(String[] args) {
Map<String, List<String>> studentsByClass = new HashMap<>();
// クラス "A" に学生を追加
studentsByClass
.computeIfAbsent("A", k -> new ArrayList<>())
.add("Tanaka");
studentsByClass
.computeIfAbsent("A", k -> new ArrayList<>())
.add("Sato");
studentsByClass
.computeIfAbsent("B", k -> new ArrayList<>())
.add("Suzuki");
System.out.println(studentsByClass);
// {A=[Tanaka, Sato], B=[Suzuki]}
}
}
Java👉 computeIfAbsent を使うと「キーがなければ新しいリストを作る」処理が簡潔に書ける。
2. 取得と走査
List<String> classA = studentsByClass.get("A");
if (classA != null) {
for (String name : classA) {
System.out.println(name);
}
}
Java3. 削除
studentsByClass.getOrDefault("A", List.of()).remove("Sato");
System.out.println(studentsByClass); // {A=[Tanaka], B=[Suzuki]}
Javaよく使う操作テンプレート
値の追加
map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
Java値の取得(存在しない場合は空リスト)
List<V> values = map.getOrDefault(key, Collections.emptyList());
Java値の削除
List<V> values = map.get(key);
if (values != null) {
values.remove(value);
if (values.isEmpty()) {
map.remove(key); // 空ならキーごと削除
}
}
Java全走査
for (Map.Entry<K, List<V>> e : map.entrySet()) {
K key = e.getKey();
List<V> vals = e.getValue();
System.out.println(key + " -> " + vals);
}
Java例題で練習
例題1: 商品カテゴリ管理
Map<String, List<String>> productsByCategory = new HashMap<>();
productsByCategory.computeIfAbsent("Fruit", k -> new ArrayList<>()).add("Apple");
productsByCategory.computeIfAbsent("Fruit", k -> new ArrayList<>()).add("Banana");
productsByCategory.computeIfAbsent("Drink", k -> new ArrayList<>()).add("Tea");
System.out.println(productsByCategory);
// {Fruit=[Apple, Banana], Drink=[Tea]}
Java例題2: 日付ごとのイベント管理
Map<String, List<String>> eventsByDate = new HashMap<>();
eventsByDate.computeIfAbsent("2025-12-08", k -> new ArrayList<>()).add("Meeting");
eventsByDate.computeIfAbsent("2025-12-08", k -> new ArrayList<>()).add("Workshop");
eventsByDate.computeIfAbsent("2025-12-09", k -> new ArrayList<>()).add("Conference");
System.out.println(eventsByDate);
// {2025-12-08=[Meeting, Workshop], 2025-12-09=[Conference]}
Java例題3: 集計処理(Map + Stream)
Map<String, List<Integer>> scores = new HashMap<>();
scores.computeIfAbsent("Math", k -> new ArrayList<>()).addAll(List.of(80, 90, 70));
scores.computeIfAbsent("English", k -> new ArrayList<>()).addAll(List.of(85, 75));
scores.forEach((subject, list) -> {
double avg = list.stream().mapToInt(Integer::intValue).average().orElse(0);
System.out.println(subject + " 平均=" + avg);
});
Java実務でのコツ
- 初期化の簡略化:
computeIfAbsentを必ず覚える。 - 空リスト返却:
getOrDefault(key, Collections.emptyList())を使うと安全。 - 削除時の空チェック: 値リストが空になったらキーごと削除すると管理が楽。
- 大量データ: Guava の
Multimapや Apache Commons Collections のMultiValuedMapを使うと便利。
まとめ
- MultiMap パターンは「Map<K, List<V>>」で 1対多を表現する。
- 追加は
computeIfAbsent+addが定番。 - 取得は
getOrDefaultで安全。 - 削除や走査もシンプルに書ける。
👉 練習課題として「クラスごとの学生名簿を作り、各クラスの人数を出力する」プログラムを書いてみると、MultiMap パターンの便利さが体感できます。
