Sobes.tech
Middle

Tell about the contract of hashCode and equals methods in Java.

sobes.tech AI

Answer from AI

The contract of hashCode and equals methods in Java defines their relationship and guarantees correct object behavior when using collections based on hash tables (e.g., HashSet, HashMap).

Main rules of the contract:

  1. Reflexivity: For any non-null reference value x, x.equals(x) should return true.
  2. Symmetry: For any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  3. Transitivity: For any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  4. Consistency: For any non-null reference values x and y, multiple invocations of x.equals(y) should consistently return the same result, provided no information used in equals comparisons on the objects is modified.
  5. Non-nullity: For any non-null reference value x, x.equals(null) should return false.

And the main rule linking hashCode and equals:

  • If two objects are equal according to the equals(Object) method, then calling hashCode() on each of these objects must produce the same integer.
  • The reverse is not necessarily true: if two objects have the same hash code, they are not necessarily equal.

Contract significance:

Violation of the contract can lead to incorrect behavior of collections. For example, if two equal objects return different hash codes, a HashMap might place them in different "buckets" and fail to find one object by the other, even if they are logically equal.

Example of a correct implementation:

// Example class with correct equals and hashCode
public final class Point {
    private final int x;
    private final int y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Point point = (Point) o;
        return x == point.x && y == point.y;
    }

    @Override
    public int hashCode() {
        int result = 17; // Magic number, usually prime
        result = 31 * result + x;
        result = 31 * result + y;
        return result;
    }

    // Getters (not mandatory for equals/hashCode example)
    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }
}

Usage:

  • Always override both methods (equals and hashCode) together.
  • When computing hashCode, use the same fields used in equals.
  • Strive to create "good" hash functions that distribute hash codes evenly to minimize collisions (when different objects have the same hash code) and improve the performance of hash-based collections.
  • For primitives, use their values. For objects, call their hashCode(). For arrays, use methods from Arrays.

In Java 7+, for convenience and correctness, it is recommended to use Objects.equals(Object a, Object b) and Objects.hash(Object... values).

Example with Objects:

// Example class with equals and hashCode using Objects
import java.util.Objects;

public final class PointWithObjects {
    private final int x;
    private final int y;

    public PointWithObjects(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PointWithObjects that = (PointWithObjects) o;
        return x == that.x && y == that.y; // Comparison logic remains
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y); // Convenient method for hash calculation
    }
}