【Java】JavaのMapとは?HashMap・TreeMap・LinkedHashMapの違いを解説

Java

こんにちは、かつコーチです。
今回は「キーと値のペアでデータを管理する」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 というコンパイルエラーになります。
MapListSetと違い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()を経由する
  • mergecomputeIfAbsentを使うと集計処理がシンプルになる

次に読むべき記事

  • Setの基本:HashSetとTreeSetの違い
  • Comparator・Comparableで並び替えをカスタマイズする

タグ: Java, 初心者向け, コレクション

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