こんにちは、かつコーチです。
今回は「キーと値のペアでデータを管理する」Mapについて解説します。
実装クラスが3種類あって混乱しやすいポイントなので、それぞれの特徴とつまずきやすい落とし穴を、実際のコード付きで整理していきましょう。
Mapとは?
キーと値のペアで管理する仕組み
Map(キーと値を1組にして管理するインターフェース)は、ListやSetとは違い「番号」ではなく「キー」でデータにアクセスします。
import java.util.HashMap;
import java.util.Map;
Map<String, Integer> scores = new HashMap<>();
scores.put("かつコーチ", 90);
scores.put("Bさん", 75);
System.out.println(scores.get("かつコーチ")); // 90
「名前で点数を引きたい」「商品コードで在庫数を引きたい」のように、何かのキーに対応する値を素早く取り出したいときに使う、非常に登場頻度の高いコレクションです。
なぜ複数の実装クラスがあるのか
MapもListやSet同様インターフェースであり、実装クラスによって「順序の扱い方」が異なります。
用途に応じて使い分けることで、無駄のないコードが書けます。
基本の書き方
HashMapの基本操作
Map<String, Integer> inventory = new HashMap<>();
inventory.put("りんご", 10);
inventory.put("バナナ", 5);
inventory.put("りんご", 8); // 同じキーは上書きされる
System.out.println(inventory.get("りんご")); // 8
System.out.println(inventory.getOrDefault("みかん", 0)); // 0(キーがなければデフォルト値)
HashMapは最も一般的に使われる実装で、キーの順序は保証されません。getOrDefaultはキーが存在しない場合の初期値を安全に取得できる、実務でよく使うメソッドです。
LinkedHashMapの基本操作
Map<String, Integer> ordered = new java.util.LinkedHashMap<>();
ordered.put("1番目", 1);
ordered.put("2番目", 2);
ordered.put("3番目", 3);
for (Map.Entry<String, Integer> entry : ordered.entrySet()) {
System.out.println(entry.getKey() + " = " + entry.getValue());
}
// 1番目 = 1
// 2番目 = 2
// 3番目 = 3(追加した順で出力される)
LinkedHashMapは追加した順序をそのまま保持するのが特徴です。
「表示順を制御したいが検索も速くしたい」というときに向いています。
TreeMapの基本操作
Map<String, Integer> sorted = new java.util.TreeMap<>();
sorted.put("バナナ", 5);
sorted.put("りんご", 10);
sorted.put("みかん", 3);
System.out.println(sorted);
// キーのソート順(文字コード順)で並ぶ
TreeMapはキーを常にソートされた状態で保持します。
「ランキング表示のようにキー順で並べたい」場合に使います。
3種類の使い分け表
| 実装クラス | 順序の扱い | 速度 | 主な用途 |
|---|---|---|---|
| HashMap | 保証なし | 速い | 順序を気にしない一般的な用途 |
| LinkedHashMap | 追加順を保持 | やや速い | キャッシュ、表示順の制御 |
| TreeMap | キーでソート | やや遅い | ランキングや範囲検索 |
よくあるつまずきポイント・エラー対処
for文でMapをそのまま回そうとしてエラーになった
初心者の方によくある間違いとして、Mapを配列のように直接forEachで回そうとするケースがあります。
// ❌Before:Mapは直接反復できない
Map<String, Integer> scores = new HashMap<>();
scores.put("かつコーチ", 90);
for (String key : scores) { // コンパイルエラー
System.out.println(key);
}
これを実行すると error: for-each not applicable to expression type というコンパイルエラーになります。MapはListやSetと違いIterableを直接実装していないため、そのままfor文には渡せません。
// ✅After:entrySet・keySet・valuesのいずれかを使う
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// キーだけ欲しい場合
for (String key : scores.keySet()) {
System.out.println(key);
}
「Mapを回すときはentrySet()かkeySet()を経由する」と覚えておけば、このエラーは避けられます。
応用・一歩先の使い方
値の更新をシンプルに書くメソッド
Map<String, Integer> wordCount = new HashMap<>();
String[] words = {"java", "python", "java", "java", "python"};
for (String word : words) {
wordCount.merge(word, 1, Integer::sum);
}
System.out.println(wordCount); // {java=3, python=2}
mergeメソッドを使うと、「キーが存在しなければ初期値をセット、存在すれば加算」という処理を1行で書けます。
単語の出現回数カウントのような集計処理でよく使われるパターンです。
computeIfAbsentでネストしたMapを扱う
Map<String, List<String>> groupByCategory = new HashMap<>();
groupByCategory.computeIfAbsent("果物", k -> new ArrayList<>()).add("りんご");
groupByCategory.computeIfAbsent("果物", k -> new ArrayList<>()).add("バナナ");
System.out.println(groupByCategory); // {果物=[りんご, バナナ]}
computeIfAbsentは「キーがなければ新しいListを作って登録し、あればそのListを返す」処理を安全に行えます。
自分でnullチェックを書くよりも簡潔で、バグも減らせます。
まとめ
この記事のポイント
- Mapはキーと値のペアでデータを管理するインターフェース
- HashMapは順序を保証しない代わりに高速
- LinkedHashMapは追加順を保持する
- TreeMapはキーの順序でソートされる
- Mapを反復するときは
entrySet()やkeySet()を経由する mergeやcomputeIfAbsentを使うと集計処理がシンプルになる
次に読むべき記事
- Setの基本:HashSetとTreeSetの違い
- Comparator・Comparableで並び替えをカスタマイズする
タグ: Java, 初心者向け, コレクション