論理的な等価性を定義するために equals() をオーバーライドするとき、同時に hashCode() もオーバーライドする必要があります — ハッシュベースのコレクション (, ) は、というコントラクトに依存しているためです。このルールを破ると、微妙で見つけづらいバグが発生します。
HashMapHashSet// by default, equals() compares IDENTITY (same object?), hashCode() is based on memory address
Person p1 = new Person("Ann", 30);
Person p2 = new Person("Ann", 30);
p1.equals(p2); // false by default — different objects, even with same data
オーバーライドなしでは、同じコンテンツを持つ 2 つのオブジェクトは「等しくない」と見なされます — 通常、値オブジェクトでは望ましくありません。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person p = (Person) o;
return age == p.age && Objects.equals(name, p.name); // compare by CONTENT
}
@Override
public int hashCode() {
return Objects.hash(name, age); // MUST be consistent with equals()
}
コントラクト: a.equals(b) が true の場合、a.hashCode() == b.hashCode() は必ず true である必要があります。
Person p1 = new Person("Ann", 30);
Person p2 = new Person("Ann", 30); // equal by our equals()
Set<Person> set = new HashSet<>();
set.add(p1);
set.contains(p2); // ❌ likely FALSE — even though p1.equals(p2)!
なぜでしょうか?HashMap/HashSet はまず hashCode() を使って正しいバケットを見つけ、その中で equals() を使います。デフォルト(アイデンティティベース)の hashCode() では、p1 と p2 は異なるバケットに格納されるため、contains はそれらを比較することさえありません — セットはそれらが異なると考えます。これは奇妙なバグを生成します:Set 内の重複、マップ検索の失敗など。
1. equal objects → equal hash codes (REQUIRED for correctness)
2. unequal objects MAY have the same hash (collisions are allowed)
3. hashCode() must be consistent (same object → same code, unchanged)
4. equals() must be reflexive, symmetric, transitive, consistent
record Person(String name, int age) {} // records auto-generate equals/hashCode/toString!
Java のレコード(および IDE 生成 / Lombok)は、正しく一貫性のある equals/hashCode を自動的に生成します。
equals/hashCode コントラクトは、Java で最も重要であり、かつ最も頻繁に違反されるルールの 1 つです。
equals() を hashCode() なしでオーバーライドすると、ハッシュベースのコレクションが壊れます。その方法は、デバッグが難しいものです:等しいと考えるオブジェクトが HashMap/HashSet 内で「失われ」ます(検索失敗、幻のような重複)。これは異なるバケットにハッシュされるためです。
これらのコレクションはどこにでもあるため、キーやセット要素として使用されるすべての値型クラスは、両方を一貫してオーバーライドする必要があります。
理由(バケット検索メカニズムの後の等価性)を理解し、レコードまたは生成されたコードを使用してそれを正しく行うことは、正しい動作に必須であり、Java コレクションがどのように機能するかについての実際の理解を示す典型的なインタビュー トピックです。