← Skill tree CS Skill Tree 0 CSCD210

equals and hashCode

Textbook: BJP (Reges and Stepp)

Where you are: Week 0 review > equals and hashCode

Try This First

A Book overrides equals to compare title and price, but does not override hashCode. Predict the size after adding two equal books to a HashSet:

Book b1 = new Book("Java", 30.00);
Book b2 = new Book("Java", 30.00);
HashSet<Book> set = new HashSet<>();
set.add(b1);
set.add(b2);
System.out.println(set.size());
Reveal

2, even though b1.equals(b2) is true. The set uses hashCode to pick a bucket. The inherited hashCode is identity-based, so the two books land in different buckets, equals is never called between them, and the set keeps both. Overriding equals without hashCode causes this silent loss.

Before You Start

Check each box you can do from memory. A box you cannot check yet is not a problem; it points you to a quick refresher, not a grade.

See References: Arrows, ==, and Aliasing if either item feels uncertain.

Not sure? Take the 60-second self-check.

Try each from memory, then read the answer under it.

  1. What does a variable of a class type actually hold? A reference (an arrow) to an object, not the object itself.
  2. What does == compare for two object references? Whether they point to the same object, not whether the contents match.

What You Need To Walk In With

Walk into the next class able to state these:

You should be able to: override equals and hashCode on the same fields, and explain why both are needed.

How It Works

equals compares contents

@Override
public boolean equals(Object obj) {
    if (this == obj) { return true; }
    if (!(obj instanceof Book other)) { return false; }   // false for null and wrong type
    return Objects.equals(title, other.title)
        && Double.compare(price, other.price) == 0;
}

The parameter is Object. The instanceof check rejects null and the wrong type, then casts. The body compares the same fields you consider meaningful.

hashCode travels with equals

@Override
public int hashCode() {
    return Objects.hash(title, price);   // the SAME fields equals compares
}

A hash-based collection calls hashCode to choose a bucket, then equals within that bucket. If two equal objects return different hash codes, they land in different buckets and are never compared, so the set treats them as distinct. Equal objects must share a hash code.

Override, not overload

public boolean equals(Book other) { ... }   // WRONG: this is an overload

This does not override Object.equals(Object); it is a separate method with a different parameter type. Collections call equals through an Object reference, so they use the inherited version and ignore yours. The bug hides in tests that call b1.equals(b2) with Book references, where the compiler picks the overload. Writing @Override above the method forces a compile error unless the signature is the real equals(Object).

Worked Example: Predict, Then Check

A Book correctly overrides both equals and hashCode on title and price. Predict:

HashSet<Book> set = new HashSet<>();
set.add(new Book("Java", 30.00));
System.out.println(set.contains(new Book("Java", 30.00)));
Reveal

true. Equal fields give the same hash code (same bucket) and equals returns true, so the set finds the matching book. This is the behavior the inherited hashCode would have broken.

A Common Mistake

Overriding equals and forgetting hashCode is the classic silent bug: direct b1.equals(b2) calls look correct, but a HashSet or HashMap stores duplicates and reports contains as false for an equal object. Override both, on the same fields, every time. (Source: BJP (Reges and Stepp), Ch 8; Effective Java, Items 10 and 11.)

Go Deeper (optional)

For the curious: the contract runs one direction only. Equal objects must share a hash code, but two unequal objects may share one (a collision), and the collection handles that by checking equals within the bucket. That is why a weak hashCode (say, always returning 0) is correct but slow: every object lands in one bucket, turning fast lookups back into a linear scan.

Check Yourself

Close the notes and answer each one from memory, then reveal it. Pulling an idea back from memory is one of the strongest ways to make it stick.

Check your understanding

A Book overrides equals but not hashCode. Two equal books are added to a HashSet. What is set.size()?

Tier 2 · Effective Java, Item 11

When you override equals, what else must you override?

Tier 1 · Effective Java, Item 11

What is the parameter type of a correct equals override?

Tier 2 · Effective Java, Item 10

How does @Override help when writing equals?

Tier 1 · Effective Java, Item 40