Když přepíšete equals() pro definování logické rovnosti, musíte také přepsat hashCode() — protože kolekce založené na hash (HashMap, ) se opírají o smlouvu, že . Porušení toho vede k jemným chybám, které jsou obtížně nalézavné.
HashSet// 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
Bez přepsání jsou dva objekty se shodným obsahem "nerovné" — obvykle ne to, co chcete u objektů představujících hodnoty.
@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()
}
Smlouva: jestliže a.equals(b) je true, pak a.hashCode() == b.hashCode() MUSÍ být 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)!
Proč? HashMap/HashSet nejdříve používá hashCode() k nalezení správného bucketu, poté equals() v něm. S výchozím (založeným na identitě) hashCode(), p1 a p2 skončí v různých bucketech, takže contains je nikdy neporovnává — množina si myslí, že jsou různé. To způsobuje matoucí chyby: duplikáty v Set, selhání vyhledávání mapy atd.
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 records (a vygenerování IDE / Lombok) vyrábí správné a konzistentní equals/hashCode za vás.
Smlouva equals/hashCode je jedním z nejdůležitějších — a nejčastěji porušovaných — pravidel Java.
Přepsání equals() bez hashCode() porušuje kolekce založené na hash způsobem, který je těžko debugovat: objekty, které považujete za rovné, se "ztratí" v HashMap/HashSet (selhání vyhledávání, fantómové duplikáty), protože jsou hashují do různých bucketů.
Vzhledem k tomu, že tyto kolekce jsou všudypřítomné, každá třída typu hodnota používaná jako klíč nebo prvek množiny musí přepsat obojí konzistentně.
Rozdíl proč (mechanismus vyhledávání bucket-a-pak-rovnosti) — a používání records nebo vygenerovaného kódu, aby to bylo správně — je nezbytný pro správné chování a klasické téma rozhovoru, které odhaluje skutečné porozumění tomu, jak kolekce Java fungují.