Java | Java 標準ライブラリ:Map / HashMap

Java Java
スポンサーリンク

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
Java

List なら 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 が複数あっても問題なし
Java

HashMap の基本操作: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);
}
Java

keySet() は「すべてのキーの集合(Set)」を返します。
「登録されている名前一覧」「ID 一覧」などが欲しいときに使います。

values:値だけ欲しいとき

for (String tel : phoneBook.values()) {
    System.out.println("電話番号: " + tel);
}
Java

values() は「すべての値のコレクション」を返します。
値は重複していても構わないので、戻り値は Collection<V> です。

entrySet:キーと値のペアを両方使いたいとき(これが一番よく使う)

for (Map.Entry<String, String> entry : phoneBook.entrySet()) {
    String name = entry.getKey();
    String tel  = entry.getValue();
    System.out.println(name + " → " + tel);
}
Java

entrySet() は「キーと値のペア(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 以降は getOrDefaultmerge でさらに綺麗に書けますが、
初心者のうちはまずこの基本パターンを体に入れるのが大事です。


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} など順番バラバラ
Java

HashMap挿入順を保持しません

  • 「入れた順に出てくるだろう」と期待してはいけない
  • 表示された順番に意味を持たせてはいけない

順番も大事にしたいなら、LinkedHashMap(挿入順を保持)や TreeMap(キー順で保持)を使います。

キーは equals / hashCode で識別される(自作クラスのとき注意)

HashMap は内部でハッシュテーブルを使っていて、
キーの hashCode()equals() で同一性を判断しています。

  • String, Integer などの標準クラス → すでに正しく実装済み
  • 自分のクラスをキーにする → equals / hashCode をちゃんと実装しないとヤバい

自作クラスをキーにして、「同じ ID のオブジェクトは同一と扱いたい」なら、

  • id が同じなら equalstrue
  • hashCodeid から計算

というように実装する必要があります。

これをやらないと、

  • 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 でキーだけ・値だけ・ペアごとにループできる
  • 出現回数カウントなど、「何かに対する数値・状態」を記録する用途で非常に強い

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