# Identity, Equality and Hashing — Object-Oriented Programming (OOP)

Source: https://www.skillbyai.com/en/oop/o-equality

> Implement equals and hashCode correctly and understand identity versus value equality.

## Same object or equal objects?

**Identity** asks whether two references point to the **same object** (`==` for objects in Java, `is` in Python). **Equality** asks whether two objects **represent the same value** (`equals` in Java, `==` via `__eq__` in Python). By default, Java's `Object.equals` uses identity, so two `Money(500, "INR")` objects are not equal unless you override it. The **equals contract** requires equality to be **reflexive**, **symmetric**, **transitive** and **consistent**, and `x.equals(null)` must be false. Crucially, if you override `equals` you must override **`hashCode`** so that **equal objects have equal hash codes**; otherwise hash-based collections (`HashMap`, `HashSet`) misbehave, failing to find keys that are "equal". Base equality on fields that define identity in the domain, and avoid mutable fields in objects used as hash keys. Java **records** and Kotlin **data classes** generate correct `equals` and `hashCode` automatically; in Python, defining `__eq__` without `__hash__` makes instances unhashable, and `@dataclass(frozen=True)` generates both.

## Identity versus equality

Two references to one object are identical; two objects with the same value are equal but not identical.

![Left: two arrows pointing to one box. Right: two separate boxes with identical contents, each with its own arrow.](assets/figures/oop/section-7-map.svg) — Figure 7.1 — Identity (same object) compared with equality (same value).

## equals and hashCode done right

Equal objects must produce equal hash codes.

```java
import java.util.Objects;

public final class Money {
    private final long paise;
    private final String currency;

    public Money(long paise, String currency) {
        this.paise = paise;
        this.currency = Objects.requireNonNull(currency);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Money other)) return false;
        return paise == other.paise && currency.equals(other.currency);
    }

    @Override
    public int hashCode() {
        return Objects.hash(paise, currency);
    }
}

var set = new java.util.HashSet<Money>();
set.add(new Money(500, "INR"));
boolean found = set.contains(new Money(500, "INR"));   // true only because hashCode is overridden too

// equivalent and shorter:
// public record Money(long paise, String currency) { }
```

## Never compare strings with == in Java

`==` on strings compares references; it may appear to work for literals because of string interning, then fail for strings read from input. Use `equals` (or `Objects.equals` to handle nulls).

**Quiz:** If a Java class overrides equals but not hashCode, what typically goes wrong?

- [x] Equal objects may land in different hash buckets, so HashSet and HashMap lookups fail
- [ ] The class no longer compiles
- [ ] equals stops being called
- [ ] Objects become immutable

*Answer:* Equal objects may land in different hash buckets, so HashSet and HashMap lookups fail. The equals-hashCode contract requires equal objects to have equal hash codes.
