Map / HashMap をざっくりイメージする
まず「Map って何者?」からいきます。
Map は
「キー(key)と値(value)のペアを管理する入れ物」
です。
電話帳をイメージすると分かりやすいです。
- 「名前」 → キー
- 「電話番号」 → 値
という対応関係を Map で持ちます。
HashMap は、この Map インターフェースを実装した代表的なクラスで、
- キーは一意(同じキーは 1 回だけ)
- 値は重複してもよい
- 順番は保証しない(入れた順には出てこない)
- 追加・検索・削除がとても高速
という特徴を持ちます。
コードでは、だいたいこう書きます。
import java.util.Map;
import java.util.HashMap;
Map<String, String> phoneBook = new HashMap<>();
Java左側を Map、右側を HashMap にするのが“お作法”です。
List / Set と Map の決定的な違い
List / Set は「値だけの集まり」、Map は「キーと値のペア」
List<String>→ 値が順番に並んだだけ(0 番目、1 番目…)Set<String>→ 重複なしの集合(順番はどうでもいい)Map<K, V>→ 「キー → 値」のペアの集まり
Map は「インデックス」ではなく「キー」で値を取り出します。
Map<String, String> phoneBook = new HashMap<>();
phoneBook.put("山田", "090-1111-2222");
phoneBook.put("佐藤", "080-3333-4444");
System.out.println(phoneBook.get("山田")); // 090-1111-2222
JavaList なら list.get(0) のように位置で取りますが、
Map は「山田」というキーで直接引けるのが本質です。
キーは一意、値は重複 OK
Map では「キー」が一意です。
Map<String, Integer> scores = new HashMap<>();
scores.put("英語", 85);
scores.put("数学", 90);
scores.put("英語", 95); // 同じキーに再度 put
Javaこの場合、「英語」の値は 85 → 95 に上書きされます。
「同じ科目(キー)が 2 件」は存在できません。
一方で値は重複 OK です。
scores.put("英語", 95);
scores.put("国語", 95); // 値 95 が複数あっても問題なし
JavaHashMap の基本操作:put / get / remove / containsKey
put:キーと値のペアを登録・更新する
Map<String, String> phoneBook = new HashMap<>();
phoneBook.put("山田", "090-1111-2222");
phoneBook.put("佐藤", "080-3333-4444");
phoneBook.put("山田", "070-5555-6666"); // 「山田」を上書き
Java同じキーで put すると、「新しい値で上書き」という動きになります。
「登録 or 更新」をまとめて 1 つのメソッドでやってくれる感覚です。
get:キーから値を取り出す(存在しないと null)
String number = phoneBook.get("山田");
System.out.println(number); // 070-5555-6666(最後に入れた方)
String unknown = phoneBook.get("鈴木");
System.out.println(unknown); // null(そのキーはない)
Java存在しないキーを get すると null が返ります。
ここをちゃんと意識しておかないと、NullPointerException を食らいがちです。
remove:キーごと削除する
phoneBook.remove("佐藤"); // 佐藤→080-3333-4444 のペアが消える
Java存在しないキーを remove しても例外にはなりません。
戻り値として「削除された値」や null が返るオーバーロードもあります。
containsKey / containsValue:存在チェック
if (phoneBook.containsKey("山田")) {
System.out.println("山田さんは登録済み");
}
if (phoneBook.containsValue("090-1111-2222")) {
System.out.println("この番号は誰かに使われている");
}
Java- 「キーがあるか?」 →
containsKey - 「値があるか?」 →
containsValue
containsKey は HashMap の得意技(高速)ですが、containsValue は内部的に総なめになるので遅めです。
実務では「キーで探す」設計をすることが多いです。
Map を「回す」ための 3 パターン:keySet / values / entrySet
Map は List みたいに for (int i=0; i<...; i++) とは回せません。
代わりに、目的に応じて 3 つの視点を使い分けます。
keySet:キーだけ欲しいとき
for (String name : phoneBook.keySet()) {
System.out.println("名前: " + name);
}
JavakeySet() は「すべてのキーの集合(Set)」を返します。
「登録されている名前一覧」「ID 一覧」などが欲しいときに使います。
values:値だけ欲しいとき
for (String tel : phoneBook.values()) {
System.out.println("電話番号: " + tel);
}
Javavalues() は「すべての値のコレクション」を返します。
値は重複していても構わないので、戻り値は Collection<V> です。
entrySet:キーと値のペアを両方使いたいとき(これが一番よく使う)
for (Map.Entry<String, String> entry : phoneBook.entrySet()) {
String name = entry.getKey();
String tel = entry.getValue();
System.out.println(name + " → " + tel);
}
JavaentrySet() は「キーと値のペア(Entry)の集合」を返します。
「両方使って何かしたい」なら、基本的に entrySet 一択です。
典型的な使い道:出現回数カウント(頻度表)
文字列の各文字の出現回数を数える例
Map の超ド定番パターンが「出現回数のカウント」です。
import java.util.HashMap;
import java.util.Map;
public class CharCount {
public static void main(String[] args) {
String text = "banana";
Map<Character, Integer> countMap = new HashMap<>();
for (char c : text.toCharArray()) {
Integer count = countMap.get(c);
if (count == null) {
countMap.put(c, 1);
} else {
countMap.put(c, count + 1);
}
}
System.out.println(countMap); // {a=3, b=1, n=2} のようなイメージ
}
}
Java説明すると、
- キー:文字(
Character) - 値:その文字が出てきた回数(
Integer)
という Map を作っています。
get して null なら初回(1 回目)、
そうでなければ 1 足して put し直す、という流れです。
Java 8 以降は getOrDefault や merge でさらに綺麗に書けますが、
初心者のうちはまずこの基本パターンを体に入れるのが大事です。
HashMap の特徴と注意点(ここを深く理解しておくと強い)
HashMap は順番を保証しない
Map<String, Integer> map = new HashMap<>();
map.put("apple", 3);
map.put("banana", 5);
map.put("orange", 2);
System.out.println(map); // {orange=2, apple=3, banana=5} など順番バラバラ
JavaHashMap は挿入順を保持しません。
- 「入れた順に出てくるだろう」と期待してはいけない
- 表示された順番に意味を持たせてはいけない
順番も大事にしたいなら、LinkedHashMap(挿入順を保持)や TreeMap(キー順で保持)を使います。
キーは equals / hashCode で識別される(自作クラスのとき注意)
HashMap は内部でハッシュテーブルを使っていて、
キーの hashCode() と equals() で同一性を判断しています。
String,Integerなどの標準クラス → すでに正しく実装済み- 自分のクラスをキーにする →
equals/hashCodeをちゃんと実装しないとヤバい
自作クラスをキーにして、「同じ ID のオブジェクトは同一と扱いたい」なら、
idが同じならequalsはtruehashCodeもidから計算
というように実装する必要があります。
これをやらないと、
putしたのにgetで取れない- 同じキーのつもりが、Map から見れば全部別物
という不思議な挙動になります。
null キー・null 値について
HashMap は
- null をキーに 1 つだけ使える
- null を値として使うこともできる
という仕様です。
ただし、null キーは 1 つだけなので、
あまり積極的に使うのはおすすめしません。
「存在しないキーの get も null を返す」ので、
「キーはあるけど値が null」と「キー自体が存在しない」が区別しづらくなります。
現場では「キーも値も null は基本的に入れない設計」にすることが多いです。
まとめ:Map / HashMap を頭の中でどう位置づけるか
初心者向けに整理すると、Map / HashMap はこういう存在です。
- Map は「キー → 値」のペアを管理するデータ構造
- HashMap はその代表実装で、「順番は保証しないが、追加・検索・削除が高速」
- キーは一意で、同じキーに put すると上書きされる
- get で取り出し、remove で削除、containsKey で存在チェック
- keySet / values / entrySet でキーだけ・値だけ・ペアごとにループできる
- 出現回数カウントなど、「何かに対する数値・状態」を記録する用途で非常に強い
