AIによる要約
HashMapに値を入れたのにgetで取れない場合、キーに使っているクラスのequalsとhashCodeが原因かもしれません。この記事では、初学者向けにMapの仕組みと現場で起きる失敗を整理します。


業務システムでは、ユーザーID、商品コード、注文番号などをキーにしてHashMapへ値を入れることがあります。
ところが、自分で作ったクラスをキーにした瞬間に、putした値がgetできない問題が起きることがあります。
HashMapはキーをhashCodeとequalsで探す- 同じ値に見えても、同一判定が壊れていると値を取得できない
- Mapのキーにするクラスは、値が変わらない設計にする
- レビューでは、キー用クラスの比較基準と変更可能性を見られる
まずHashMapはキーをどう探すのか
HashMapは、キーをそのまま順番に全部見て探しているわけではありません。まずhashCodeで大まかな場所を探し、その場所の中でequalsを使って同じキーかを確認します。
そのため、equalsでは同じに見えるのにhashCodeが違う、またはhashCodeが同じでもequalsが違う、という状態になると期待通りに値を取れません。

Map<UserId, String> names = new HashMap<>(); names.put(new UserId("U001"), "田中"); String name = names.get(new UserId("U001")); System.out.println(name);
見た目には同じU001を指定しています。しかし、UserIdがequalsとhashCodeを正しく実装していなければ、get結果はnullになることがあります。
よくある原因1:equalsだけ実装している
Java新人がやりがちなのは、equalsだけを実装してhashCodeを忘れるパターンです。Listのcontainsでは動いたように見えるため、HashMapで初めて問題に気づくことがあります。
public class UserId {
private final String value;
public UserId(String value) {
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof UserId other)) {
return false;
}
return Objects.equals(this.value, other.value);
}
}
このクラスはequalsだけを上書きしています。HashMapやHashSetで使うなら、hashCodeも同じ基準で実装する必要があります。
public class UserId {
private final String value;
public UserId(String value) {
this.value = Objects.requireNonNull(value);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof UserId other)) {
return false;
}
return Objects.equals(this.value, other.value);
}
@Override
public int hashCode() {
return Objects.hash(value);
}
}
大事なのは、equalsで比較したvalueと、hashCodeに使うvalueが揃っていることです。
よくある原因2:Mapに入れたあとでキーを変更している
もう1つ現場で危ないのが、キーに使っているオブジェクトの値を後から変更するケースです。HashMapは登録時のハッシュ値を前提に管理しているため、キーの中身が変わると取り出せなくなることがあります。
UserKey key = new UserKey("U001"); Map<UserKey, String> names = new HashMap<>(); names.put(key, "田中"); key.setValue("U002"); System.out.println(names.get(key)); // 取れないことがある
これはかなり厄介です。コード上は同じkey変数を使っているので、初学者ほど原因に気づきにくくなります。
Mapのキーにするなら避けたい設計
- setterでキー値を変更できる
equalsやhashCodeに使うフィールドが可変- 業務上の一時状態までキーの同一判定に含めている
- DTOやEntityをそのままMapのキーにしている
現場ではどんな不具合になるか
たとえば、注文番号をキーにして明細を集約する処理を考えます。注文番号が同じなら同じ注文としてまとめたいのに、キーの同一判定が壊れていると、同じ注文が複数件として扱われます。
Map<OrderNo, List<OrderDetail>> detailMap = new HashMap<>();
for (OrderDetail detail : details) {
OrderNo orderNo = new OrderNo(detail.getOrderNo());
detailMap.computeIfAbsent(orderNo, key -> new ArrayList<>()).add(detail);
}
このような処理でOrderNoのequals/hashCodeが壊れていると、集約結果がズレます。画面表示だけならまだ気づけるかもしれませんが、請求、在庫、帳票に流れると影響が大きくなります。
レビューでは何を見られるのか
レビューでは、HashMapを使っていること自体よりも、キーにしているクラスが安全かを見られます。特に、新人のコードでは「そのクラスをMapのキーにして大丈夫か」という観点でコメントが付きます。
// レビューコメント例 // UserSearchConditionをHashMapのキーにしていますが、setterで値が変わります。 // Map登録後に値が変わると取得できなくなる可能性があるため、 // キー専用の不変クラスを作るか、StringのuserIdをキーにしてください。
このコメントは、文法の指摘ではありません。業務データの集約や検索結果が壊れないようにするための設計指摘です。
迷ったときの判断基準
HashMapのキーにしてよいか確認する
- キーに使う値が業務上一意か
- キー値が生成後に変わらないか
equalsとhashCodeが同じフィールドを見ているか- DTOやEntityを流用せず、キー専用クラスにできないか
- 単純に
StringやLongをキーにした方が読みやすくないか
新人のうちは、無理に自作クラスをMapのキーにしない判断も大事です。注文番号やユーザーIDがStringで十分なら、まずは単純な型をキーにする方が読みやすいこともあります。
この記事とあわせて読みたい
まとめ
HashMapで値が取れないときは、putとgetのコードだけでなく、キーに使っているクラスのequalsとhashCodeを確認します。特に、Mapのキーは後から値が変わらない設計にすることが重要です。