この記事の要点
• HashMap は「キー」と「値」のペアを保存するコレクション
• `put(key, value)` で登録、`get(key)` で取得、`containsKey(key)` で存在確認
• `Map.of()` で不変マップを簡潔に作成できる(Java 9以降)
Map とは
Map(マップ)は「キーと値のペア」を保存するコレクションです。他の言語では「辞書(Dictionary)」「連想配列」「ハッシュテーブル」などと呼ばれます。
| 用語 | 説明 |
|---|---|
| キー(Key) | 値を取り出すための名前札。重複不可 |
| 値(Value) | キーに対応するデータ。重複可 |
| Map | インターフェース(契約) |
| HashMap | Map を実装した代表的なクラス |
例: 「社員番号 → 氏名」「国コード → 国名」「単語 → 出現回数」など。
ポイント: List や Set は要素を「順番」または「集合」として管理しますが、Map は「キーで値を引く」ための構造です。
HashMap の基本的な使い方
1. import
import java.util.HashMap;
import java.util.Map;
2. HashMap の宣言と生成
// Java 21
Map<String, Integer> scores = new HashMap<>();
<String, Integer> は「キーの型が String、値の型が Integer」という意味です。
3. 要素を追加する(put)
scores.put("Alice", 85);
scores.put("Bob", 92);
scores.put("Charlie", 78);
| キー | 値 |
|---|---|
| Alice | 85 |
| Bob | 92 |
| Charlie | 78 |
4. 要素を取得する(get)
int aliceScore = scores.get("Alice"); // 85
存在しないキーを指定すると null が返ります。
Integer unknown = scores.get("David"); // null
注意: プリミティブ型(int)に直接代入すると NullPointerException が発生するので、ラッパークラス(Integer)で受け取るか、`getOrDefault()` を使いましょう。
5. デフォルト値を指定して取得(getOrDefault)
int davidScore = scores.getOrDefault("David", 0); // 0(デフォルト)
6. キーが存在するか確認(containsKey)
if (scores.containsKey("Bob")) {
System.out.println("Bob の成績: " + scores.get("Bob"));
}
7. 要素数を確認(size)
int count = scores.size(); // 3
8. 要素を削除(remove)
scores.remove("Charlie"); // Charlie のエントリを削除
HashMap の主要メソッド
| メソッド | 説明 | 戻り値 |
|---|---|---|
put(K key, V value) | キーと値を登録(既存キーなら上書き) | V(前の値または null) |
get(Object key) | キーに対応する値を取得 | V(存在しない場合 null) |
getOrDefault(Object key, V defaultValue) | キーに対応する値を取得(なければデフォルト値) | V |
containsKey(Object key) | キーが存在するか | boolean |
containsValue(Object value) | 値が存在するか | boolean |
remove(Object key) | キーを削除 | V(削除した値または null) |
size() | エントリ数 | int |
isEmpty() | 空かどうか | boolean |
clear() | 全削除 | void |
keySet() | 全キーの Set を取得 | Set<K> |
values() | 全値の Collection を取得 | Collection<V> |
entrySet() | 全エントリ(キーと値のペア)の Set を取得 | Set<Map.Entry<K, V>> |
HashMap の走査(forEach)
Java 8 以降、forEach メソッドで全エントリを走査できます。
// Java 21
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<String, Integer> ages = new HashMap<>();
ages.put("Alice", 25);
ages.put("Bob", 30);
ages.put("Charlie", 28);
// forEach でキーと値を取得
ages.forEach((name, age) -> {
System.out.println(name + " は " + age + " 歳");
});
}
}
出力(順序は保証されない):
Alice は 25 歳
Bob は 30 歳
Charlie は 28 歳
補足: HashMap は**順序を保証しません**。挿入順を保ちたい場合は `LinkedHashMap` を使ってください。
実践例: 単語カウント
テキスト中の単語の出現回数を数えます。
// Java 21
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
String text = "apple banana apple orange banana apple";
String[] words = text.split(" ");
Map<String, Integer> wordCount = new HashMap<>();
for (String word : words) {
// 既存の値を取得(なければ 0)して +1
int count = wordCount.getOrDefault(word, 0);
wordCount.put(word, count + 1);
}
System.out.println("単語の出現回数:");
wordCount.forEach((word, count) -> {
System.out.println(word + ": " + count + " 回");
});
}
}
出力:
単語の出現回数:
apple: 3 回
banana: 2 回
orange: 1 回
ポイント: `getOrDefault(word, 0)` により、存在しないキーでも安全に処理できます。または Java 8 以降の `merge()` メソッドも便利です。
merge() メソッド(応用)
merge() を使うとカウント処理がより簡潔になります。
// Java 21
for (String word : words) {
wordCount.merge(word, 1, Integer::sum);
}
| パラメータ | 説明 |
|---|---|
word | キー |
1 | キーが存在しない場合に設定する初期値 |
Integer::sum | 既存値と新値をマージする関数(合計) |
不変マップ(Map.of)
不変マップは、一度作ったら変更不可です。Java 9 以降、Map.of() で作成できます。
// Java 21
import java.util.Map;
Map<String, String> countryCode = Map.of(
"JP", "Japan",
"US", "United States",
"GB", "United Kingdom"
);
System.out.println(countryCode.get("JP")); // Japan
// 変更しようとするとエラー
// countryCode.put("FR", "France"); // UnsupportedOperationException
| 特徴 | 説明 |
|---|---|
| 不変 | put / remove が使えない |
| 簡潔 | キーと値を交互に並べるだけ |
| null 禁止 | キーにも値にも null は使えない |
| 最大10エントリ | Map.of() は10エントリまで。それ以上は Map.ofEntries() |
注意: 不変マップに変更操作(put / remove)を実行すると `UnsupportedOperationException` が発生します。
List / Set / Map の用途比較
これまで学んだコレクションの使い分けです。
| 種類 | 代表クラス | 特徴 | 主な用途 |
|---|---|---|---|
| List | ArrayList | 順序付き、重複可、インデックスアクセス | 学生リスト、タスクリスト |
| Set | HashSet | 順序なし、重複不可 | ユニーク要素の管理(次回解説) |
| Map | HashMap | キーと値のペア、キー重複不可 | 辞書、設定値、カウント |
ポイント: 「名前で引きたい」「IDで引きたい」ならMap。「順番が大事」ならList。「重複を排除したい」ならSet(次回)。
HashMap の内部動作(補足)
HashMap は内部でハッシュテーブルを使い、キーのハッシュ値から格納位置を決めます。
| 操作 | 計算量(平均) | 備考 |
|---|---|---|
put(K, V) | O(1) | ハッシュ衝突が少ない場合 |
get(K) | O(1) | 同上 |
containsKey(K) | O(1) | 同上 |
remove(K) | O(1) | 同上 |
補足: ハッシュ衝突が多いと性能が O(n) に劣化しますが、通常は気にする必要ありません。適切な equals() / hashCode() 実装が前提です。
まとめ
| 概念 | 説明 |
|---|---|
| Map | キーと値のペアを管理するインターフェース |
| HashMap | ハッシュテーブルベースの実装。高速(O(1)) |
| put / get / remove | 基本操作。キーで値を登録・取得・削除 |
| containsKey / getOrDefault | 安全にキーを確認・取得 |
| forEach | ラムダ式で全エントリを走査 |
| Map.of() | 不変マップを簡潔に作成(Java 9以降) |
| 順序 | HashMap は順序を保証しない。LinkedHashMap で挿入順を保持 |
次のステップ
Java入門 #18 - HashSet(重複しない集合) で、重複を許さない集合型のコレクションを学びます。
参考リソース
- Oracle HashMap API ドキュメント — HashMap 公式リファレンス
- Oracle Map API — Map インターフェース
- Java Collections Framework — 公式チュートリアル
- Effective Java (Joshua Bloch) — equals / hashCode のベストプラクティス