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}
JavaMapから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)を先に入れる。
- 対策: Streamはnull要素があるとNPEの原因。Mapの値が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と並列化の扱いに注意し、読みやすさを最優先に設計すれば、短く安全で保守しやすいコードになる。
