Java 逆引き集 | Map のストリーム処理(entrySet→stream) — キー/値単位処理

Java Java
スポンサーリンク

Map のストリーム処理(entrySet→stream) — キー/値単位処理

Mapは「キー→値」のペアを扱う構造。Stream APIと組み合わせると、条件抽出、並べ替え、集計が短く安全に書けます。初心者向けに、entrySetからのstreamを中心に例題でかみ砕いて説明します。


基本パターン(entrySet → stream)

  • ペアで処理する:
Map<String, Integer> score = Map.of("A", 10, "B", 25, "C", 15);

// 10以上だけ取り出して新しいMapへ
Map<String, Integer> filtered = score.entrySet().stream()
    .filter(e -> e.getValue() >= 10)
    .collect(java.util.stream.Collectors.toMap(
        Map.Entry::getKey,
        Map.Entry::getValue
    ));
Java
  • キーだけ/値だけ処理する:
// キー一覧(ソート)
List<String> keysAsc = score.keySet().stream()
    .sorted()
    .toList();

// 値の平均(OptionalDouble)
java.util.OptionalDouble avg = score.values().stream()
    .mapToInt(Integer::intValue)
    .average();
Java
  • 値を変換して新しいMapへ:
Map<String, String> labeled = score.entrySet().stream()
    .collect(java.util.stream.Collectors.toMap(
        Map.Entry::getKey,
        e -> "score=" + e.getValue()
    ));
Java

よく使う操作テンプレート

  • フィルタ(条件抽出)
map.entrySet().stream()
    .filter(e -> 条件(e.getKey(), e.getValue()))
    .toList();
Java
  • マッピング(型や内容の変換)
map.entrySet().stream()
    .map(e -> e.getKey() + ":" + e.getValue())
    .toList();  // ["A:10", ...]
Java
  • 並べ替え(キー/値でソート)
// 値の降順で並べ替えたエントリのリスト
List<Map.Entry<String,Integer>> sorted = map.entrySet().stream()
    .sorted(Map.Entry.<String,Integer>comparingByValue().reversed())
    .toList();
Java
  • 集計(合計・最大・グルーピング)
int sum = map.values().stream().mapToInt(Integer::intValue).sum();

// 値の範囲でグルーピング
Map<String, List<Map.Entry<String,Integer>>> group = map.entrySet().stream()
    .collect(java.util.stream.Collectors.groupingBy(
        e -> e.getValue() >= 20 ? "large" : "small"
    ));
Java
  • toMap の衝突対策(キー重複時のマージ関数必須)
// keyが重複する可能性があるなら、第三引数で解決方法を指定
var toMap = listOfPairs.stream()
    .collect(java.util.stream.Collectors.toMap(
        p -> p.key(),
        p -> p.value(),
        (a, b) -> a + b  // 重複時は加算
    ));
Java

例題で身につける

例題1: 顧客ごとの購入額ランキング(上位N件)

Map<String, Integer> sales = Map.of(
    "Tanaka", 12000, "Sato", 9000, "Kato", 12000, "Ito", 5000
);

List<Map.Entry<String,Integer>> top2 = sales.entrySet().stream()
    .sorted(Map.Entry.<String,Integer>comparingByValue().reversed()
            .thenComparing(Map.Entry::getKey)) // 同額は名前昇順
    .limit(2)
    .toList();

System.out.println(top2); // [Kato=12000, Tanaka=12000] など
Java

例題2: 値でフィルタしてキー一覧を作る

Map<String, Integer> score = Map.of("A", 10, "B", 25, "C", 15);

List<String> passed = score.entrySet().stream()
    .filter(e -> e.getValue() >= 15)
    .map(Map.Entry::getKey)
    .sorted()
    .toList();

System.out.println(passed); // [B, C]
Java

例題3: 集計とグルーピング(閾値別)

Map<String, Integer> nums = Map.of("x", 3, "y", 7, "z", 2, "w", 8);

Map<String, Long> counts = nums.entrySet().stream()
    .collect(java.util.stream.Collectors.groupingBy(
        e -> e.getValue() >= 5 ? "ge5" : "lt5",
        java.util.stream.Collectors.counting()
    ));

System.out.println(counts); // {lt5=2, ge5=2}
Java

MapからListや別構造へ変換

  • 値だけのリストへ
List<Integer> values = map.values().stream().toList();
Java
  • カスタムDTOへ
record Pair(String key, int val) {}

List<Pair> pairs = map.entrySet().stream()
    .map(e -> new Pair(e.getKey(), e.getValue()))
    .toList();
Java
  • LinkedHashMapで順序保持した新Mapに再構成
Map<String,Integer> sortedMap = map.entrySet().stream()
    .sorted(Map.Entry.<String,Integer>comparingByValue().reversed())
    .collect(java.util.stream.Collectors.toMap(
        Map.Entry::getKey,
        Map.Entry::getValue,
        (a,b) -> a,
        java.util.LinkedHashMap::new   // 挿入順保持
    ));
Java

実務でのコツと落とし穴

  • ラベル: キー重複時のtoMapエラー
    • 対策: toMapで第三引数のマージ関数を必ず指定。
  • ラベル: nullの扱い
    • 対策: Streamはnull要素があるとNPEの原因。Mapの値がnullの可能性がある場合は filter(e -> e.getValue() != null) を先に入れる。
  • ラベル: 性能と読みやすさのバランス
    • 対策: 複雑になりすぎたら素直なforループが読みやすいことも。長いチェーンは途中で変数へ受ける。
  • ラベル: 並列ストリームの慎重な使用
    • 対策: parallel() は順序やコストを理解してから。小さなMapではオーバーヘッドが勝ちやすい。

ミニテンプレート集

  • 値で降順ソートしてLinkedHashMapへ
map.entrySet().stream()
    .sorted(Map.Entry.<K,V>comparingByValue().reversed())
    .collect(java.util.stream.Collectors.toMap(
        Map.Entry::getKey, Map.Entry::getValue, (a,b)->a, java.util.LinkedHashMap::new));
Java
  • 条件でフィルタしてキー一覧
List<K> keys = map.entrySet().stream()
    .filter(e -> 条件(e.getKey(), e.getValue()))
    .map(Map.Entry::getKey)
    .toList();
Java
  • グループごとに合計を計算
Map<String, Integer> sumByGroup = map.entrySet().stream()
    .collect(java.util.stream.Collectors.groupingBy(
        e -> グループキー(e.getKey(), e.getValue()),
        java.util.stream.Collectors.summingInt(e -> e.getValue())
    ));
Java
  • キーと値の文字列連結リスト
List<String> lines = map.entrySet().stream()
    .map(e -> e.getKey() + "=" + e.getValue())
    .toList();
Java

まとめ

  • MapのStream処理は「entrySet().stream()」が基本。ペアのままフィルタ、マップ、ソート、集計が直感的に書ける。
  • 新しいMapへcollectする際は、重複キー対策(マージ関数)と順序保持(LinkedHashMap指定)を覚えておくと実務で困らない。
  • nullと並列化の扱いに注意し、読みやすさを最優先に設計すれば、短く安全で保守しやすいコードになる。
タイトルとURLをコピーしました