Java 逆引き集 | MultiMap パターン(Map>) — 1対多管理

Java Java
スポンサーリンク

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);
    }
}
Java

3. 削除

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 パターンの便利さが体感できます。

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