Ce face equals și hashCode
== pe obiecte verifică dacă e vorba de același obiect. equals verifică dacă două obiecte au aceeași valoare. Versiunea moștenită din Object face tot ==, așa că pentru clasele tale trebuie rescrisă, comparând câmpurile relevante. String, Integer, LocalDate și colecțiile au deja equals corect.
Regula de aur: dacă două obiecte sunt egale după equals, trebuie să aibă același hashCode. HashMap și HashSet caută întâi după hash, apoi verifică cu equals. Cu equals rescris, dar fără hashCode, două obiecte „egale” ajung în locuri diferite și apar duplicate. Compilat cu -Xlint, javac avertizează ([overrides]).
Ajutoare: Objects.equals(a, b) compară sigur și când valorile pot fi null, iar Objects.hash(a, b, c) combină hash-urile câmpurilor. Un record le generează automat pe amândouă din toate componentele.
Exemple
Exemplul 1: Fără equals: duplicate în HashSet
Clasa nu rescrie nici equals, nici hashCode, deci două obiecte cu același CNP sunt considerate diferite: setul are 2 elemente, iar contains nu găsește persoana.
import java.util.HashSet;
import java.util.Set;
class Persoana {
final String cnp;
final String nume;
Persoana(String cnp, String nume) {
this.cnp = cnp;
this.nume = nume;
}
}
public class Main {
public static void main(String[] args) {
Persoana a = new Persoana("2990101223344", "Ana");
Persoana b = new Persoana("2990101223344", "Ana");
System.out.println("a.equals(b): " + a.equals(b));
Set<Persoana> set = new HashSet<>();
set.add(a);
set.add(b);
System.out.println("elemente în set: " + set.size());
System.out.println("conține o Ana nouă? " + set.contains(new Persoana("2990101223344", "Ana")));
}
}
Rezultat
a.equals(b): false
elemente în set: 2
conține o Ana nouă? false
Exemplul 2: equals și hashCode scrise corect
Două persoane sunt egale dacă au același CNP. hashCode folosește același câmp, deci setul păstrează un singur element, iar căutarea în HashMap funcționează.
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
class Persoana {
final String cnp;
final String nume;
Persoana(String cnp, String nume) {
this.cnp = cnp;
this.nume = nume;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Persoana p)) return false;
return Objects.equals(cnp, p.cnp);
}
@Override
public int hashCode() {
return Objects.hash(cnp);
}
}
public class Main {
public static void main(String[] args) {
Persoana a = new Persoana("2990101223344", "Ana");
Persoana b = new Persoana("2990101223344", "Ana Maria");
System.out.println("a.equals(b): " + a.equals(b));
System.out.println("același hashCode: " + (a.hashCode() == b.hashCode()));
Set<Persoana> set = new HashSet<>(Set.of(a));
set.add(b);
System.out.println("elemente în set: " + set.size());
Map<Persoana, Double> solduri = new HashMap<>();
solduri.put(a, 350.0);
System.out.println("sold găsit cu alt obiect: " + solduri.get(new Persoana("2990101223344", "?")));
System.out.println("equals(null): " + a.equals(null));
}
}
Rezultat
a.equals(b): true
același hashCode: true
elemente în set: 1
sold găsit cu alt obiect: 350.0
equals(null): false
Exemplul 3: Records: equals și hashCode automate
Record-ul compară toate componentele. Două coordonate cu aceleași valori sunt egale și ajung o singură dată în set.
import java.util.LinkedHashSet;
import java.util.Set;
record Coordonata(double lat, double lon) { }
public class Main {
public static void main(String[] args) {
Set<Coordonata> vizitate = new LinkedHashSet<>();
vizitate.add(new Coordonata(47.16, 27.58)); // Iași
vizitate.add(new Coordonata(46.77, 23.59)); // Cluj
vizitate.add(new Coordonata(47.16, 27.58)); // Iași din nou
System.out.println(vizitate);
System.out.println("egale: " + new Coordonata(1, 2).equals(new Coordonata(1, 2)));
}
}
Rezultat
[Coordonata[lat=47.16, lon=27.58], Coordonata[lat=46.77, lon=23.59]]
egale: true
Greșeli frecvente
- Rescrii
equalsfărăhashCode: obiectele egale se dublează înHashSetși nu se găsesc înHashMap; cu-Xlintprimești[overrides] Class Persoana overrides equals, but neither it nor any superclass overrides hashCode method. - Scrii
public boolean equals(Persoana p)în loc deequals(Object o). Aceasta este o supraîncărcare, nu o rescriere, iar colecțiile o ignoră.@Overridear fi prins greșeala. - Folosești în
hashCodecâmpuri care se schimbă după ce obiectul a fost pus într-unHashSet: obiectul nu mai este găsit. Folosește câmpurifinal. - Compari șiruri cu
==:"Iași" == textpoate dafalsechiar dacă textul este același. Foloseșteequals.
Comenzi înrudite
record: record declară o clasă imutabilă pentru date: constructorul, accesorii, equals, hashCode și toString sunt generați automat.Clase și obiecte (class): O clasă (cuvântul cheie class) descrie un tip nou: câmpuri pentru date și metode pentru comportament; obiectele sunt instanțele ei.instanceof: instanceof verifică dacă un obiect este de un anumit tip; în forma nouă declară și o variabilă deja convertită la acel tip.Comparatori (Comparable și Comparator): Comparable dă ordinea naturală a unei clase (compareTo); Comparator descrie orice altă ordine și se combină cu comparing, thenComparing și reversed.