Sobes.tech
Middle

Prečo je dôležité prepísať metódy equals a hashCode v Jave?

sobes.tech AI

Odpoveď od AI

Predefinovanie equals a hashCode je dôležité pre správnu prácu s objektmi v kolekciách a pri porovnávaní.

  1. Symetria: Ak a.equals(b) vracia true, potom b.equals(a) by tiež malo byť true.
  2. Reflexivita: Pre akýkoľvek nenulový objekt a, a.equals(a) by malo byť true.
  3. Transitivita: Ak a.equals(b) a b.equals(c) vracajú true, potom by malo byť a.equals(c) tiež true.
  4. Konzistencia: Opakované volania a.equals(b) by mali konzistentne vracať true alebo false, ak sa nezmenia informácie použité v porovnaní equals.

Pre hashCode:

  1. Konzistencia: Ak informácie používané v equals nie sú zmenené, opakované volania hashCode na tom istom objekte by mali vracať rovnakú celočíselnú hodnotu.
  2. Väzba s equals: Ak sú dva objekty rovnaké podľa equals, potom ich hashCode by mal byť rovnaký.
  3. Optimalizácia: Nie je potrebné, aby ak sú dva objekty nerovnaké podľa equals, ich hashCode výsledky boli odlišné, ale generovanie odlišných hash kódov pre odlišné objekty môže zlepšiť výkon hash tabuliek.

Porušenie týchto pravidiel vedie k nepredvídateľnému správaniu pri používaní kolekcií ako HashSet, HashMap, ArrayList a ďalších, ktoré sa spoliehajú na správnu implementáciu týchto metód pre správne určenie rovnosti objektov a ich umiestnenie.

Napríklad:

// Príklad triedy bez správnych `equals` a `hashCode`
class PersonBroken {
    private String name;
    private int age;

    public PersonBroken(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // getteri vynechané pre stručnosť
}

// Príklad triedy so správnymi `equals` a `hashCode`
class PersonCorrect {
    private String name;
    private int age;

    public PersonCorrect(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PersonCorrect that = (PersonCorrect) o;
        return age == that.age &&
               Objects.equals(name, that.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
    // getteri vynechané pre stručnosť
}

// Použitie v kolekcii
List<PersonBroken> brokenList = new ArrayList<>();
brokenList.add(new PersonBroken("Alice", 30));
System.out.println(brokenList.contains(new PersonBroken("Alice", 30))); // Pravdepodobne false

List<PersonCorrect> correctList = new ArrayList<>();
correctList.add(new PersonCorrect("Alice", 30));
System.out.println(correctList.contains(new PersonCorrect("Alice", 30))); // Pravdepodobne true

Set<PersonBroken> brokenSet = new HashSet<>();
brokenSet.add(new PersonBroken("Bob", 25));
brokenSet.add(new PersonBroken("Bob", 25));
System.out.println(brokenSet.size()); // Pravdepodobne 2

Set<PersonCorrect> correctSet = new HashSet<>();
correctSet.add(new PersonCorrect("Bob", 25));
correctSet.add(new PersonCorrect("Bob", 25));
System.out.println(correctSet.size()); // Pravdepodobne 1

V príklade s PersonBroken sú objekty s rovnakými hodnotami polí považované za odlišné v kolekciách, pretože sa používajú predvolené implementácie equals (porovnáva referencie) a hashCode (generuje podľa adresy objektu). V príklade s PersonCorrect umožňujú prepísané metódy kolekciám správne určovať ekvivalentnosť objektov na základe ich obsahu.