Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Always Override hashCode When Overriding equals

Tech 2

When overriding the equals method in a class, you must also override the hashCode method. Failing to do so violates the general contract for hashCode, causing the class to malfunction in hash-based collections like HashMap and HashSet. The contract, derived from the Object specification, states:

  • Repeated calls to hashCode on an object during a single execution must return the same integer, provided no fields used in equals comparisons are modified. The value need not be consistent across different executions.
  • If two objects are equal via equals, their hashCode calls must yield the same integer.
  • Unequal objects (per equals) need not produce distinct hash codes, but distinct codes can improve hash table performance.

The critical violation when skipping hashCode override is the second rule: equal objects must share equal hash codes. Without an override, Object’s default hashCode returns arbitrary values for distinct instances, evenif equals deems them equal. For example, using a ContactNumber class (with regionCode, exchangeCode, subscriberNumber fields) as a HashMap key fails:

Map<ContactNumber, String> directory = new HashMap<>();
directory.put(new ContactNumber(212, 555, 1234), "Alice");
String name = directory.get(new ContactNumber(212, 555, 1234)); // Returns null

Here, two equal ContactNumber instances have different hash codes (from Object’s default), so get searches a different bucket. Even if buckets collide, HashMap skips equality checks due to mismatched codes.

Writing a Valid hashCode Method

A trivial but invalid implementation returns a constant (e.g., 42), forcing all objects into one bucket and degrading hash tables to linked lists (quadratic time complexity). A good hash function distributes unequal instances uniformly across integers. Use this approach:

  1. Initialize an int variable (e.g., hashResult) with the hash code of the first significant field (a field affecting equals).
  2. For each remaining significant field: a. Compute its hash code (fieldHash):
    • Primitive: Use Type.hashCode(field) (e.g., Integer.hashCode(intVal)).
    • Object reference: Recursively call hashCode if equals does; use 0 for null.
    • Array: Treat each significant element as a separate field (use Arrays.hashCode for all-significant arrays). b. Merge fieldHash into hashResult: hashResult = 31 * hashResult + fieldHash.
  3. Return hashResult.

The multiplier 31 (an odd prime) helps disperse codes; modern VMs optimize 31 * i to (i << 5) - i.

Example: ContactNumber hashCode

Applying the steps to ContactNumber:

@Override
public int hashCode() {
    int hashResult = Short.hashCode(regionCode);
    hashResult = 31 * hashResult + Short.hashCode(exchangeCode);
    hashResult = 31 * hashResult + Short.hashCode(subscriberNumber);
    return hashResult;
}

This ensures equal instances share codes and disperses unequal ones. For brevity, use Objects.hash (slower due to array/boxing overhead):

@Override
public int hashCode() {
    return Objects.hash(subscriberNumber, exchangeCode, regionCode);
}

Caching Hash Codes

For immutable classes with expensive hash computations, cache the result. Initialize lazily (thread-safe with care):

private int cachedHash; // Defaults to 0

@Override
public int hashCode() {
    if (cachedHash != 0) return cachedHash;
    int hashResult = Short.hashCode(regionCode);
    hashResult = 31 * hashResult + Short.hashCode(exchangeCode);
    hashResult = 31 * hashResult + Short.hashCode(subscriberNumber);
    cachedHash = hashResult;
    return hashResult;
}

Best Practices

  • Never exclude significant fields from hash computation (risks collisions).
  • Avoid specifying exact hash code values (allows future improvements).
  • Verify equal instances yield equal codes via unit tests.

Use tools like AutoValue or IDE generators to automate equals/hashCode implementations.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.