Sobes.tech
Middle

Почему важно переопределять методы equals и hashCode в Java?

sobes.tech AI

Answer from AI

Overriding equals and hashCode is important for correct object handling in collections and comparisons.

  1. Symmetry: If a.equals(b) returns true, then b.equals(a) should also be true.
  2. Reflexivity: For any non-null object a, a.equals(a) should be true.
  3. Transitivity: If a.equals(b) and b.equals(c) return true, then a.equals(c) should also be true.
  4. Consistency: Multiple invocations of a.equals(b) should consistently return true or false, provided no information used in equals comparisons on the objects is modified.

For hashCode:

  1. Consistency: If the information used in equals comparisons on the object has not been changed, multiple calls to hashCode on the object should return the same integer value.
  2. Relation to equals: If two objects are equal according to the equals method, then calling hashCode on each of the unequal objects should return the same integer value.
  3. Optimization: It is not required that if two objects are unequal according to equals, then calling hashCode on each of the unequal objects should produce different integer results. However, generating different hash codes for unequal objects can improve hash table performance.

Violating these rules leads to unpredictable behavior when using collections such as HashSet, HashMap, ArrayList, and others that rely on the correct implementation of these methods for proper object equality determination and placement.

For example:

// Example of a class without correct `equals` and `hashCode`
class PersonBroken {
    private String name;
    private int age;

    public PersonBroken(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // getters omitted for brevity
}

// Example of a class with correct `equals` and `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);
    }
    // getters omitted for brevity
}

// Usage in collections
List<PersonBroken> brokenList = new ArrayList<>();
brokenList.add(new PersonBroken("Alice", 30));
System.out.println(brokenList.contains(new PersonBroken("Alice", 30))); // Likely false

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

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

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

In the PersonBroken example, objects with identical field values are considered different in collections because the default equals (reference comparison) and hashCode (address-based) implementations are used. In the PersonCorrect example, overridden methods allow collections to correctly determine object equivalence based on their content.