Back to Object Class, equals & hashCode
Q5Question 5 of 9

What if hashCode() always returns a constant?

Key Concepts
  • Legal but terrible for performance – all objects land in the same bucket
  • HashMap degrades from O(1) to O(n) because every lookup scans a single long chain
  • The contract is not broken (equal objects have equal hash codes), but distribution is destroyed
  • After Java 8, long chains become balanced trees (O(log n)), but still much slower than O(1)
  • A good hashCode distributes objects evenly across buckets

Definition

It is legal but makes HashMap O(n) instead of O(1) because every object lands in the same bucket. A good hashCode distributes objects evenly.

A constant hashCode:

DIAGRAM
- **Satisfies the contract.** Equal objects have equal hash codes (trivially, all objects do).
- **Destroys performance.** Every object goes to bucket 0. HashMap becomes a linked list.
- **O(1) → O(n).** Every get/put scans the entire chain. For 1 million entries, every lookup checks all 1 million.

Filing every bank account in the same drawer. Finding any account requires searching through the entire drawer instead of going directly to the right one.

Understanding the Concepts

The problem

JAVA
@Override
public int hashCode() {
    return 42;  // legal but terrible
}

// All 1,000,000 accounts go to bucket 42 % capacity
// get(key) scans 1,000,000 entries → O(n)

The fix: distribute evenly

JAVA
@Override
public int hashCode() {
    return Objects.hash(accountId);  // good distribution
}

With a good hashCode, 1,000,000 accounts spread across ~65,000 buckets (load factor 0.75). Each bucket has ~15 entries. Lookup is nearly O(1).

What should a senior engineer know?

  • Java 8+ converts long chains (>8 entries) to balanced trees, so the worst case is O(log n) instead of O(n). But this is still much slower than O(1) with good distribution.
  • Performance testing: If your HashMap is slow, check hashCode distribution with a profiler.
  • String.hashCode() is well-distributed: s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1].

What are common mistakes?

A common mistake is overriding equals() without overriding hashCode(), which breaks HashMap and HashSet behavior.

Wrong approach:

JAVA
package com.bank.model;

public class Account {
    private String accountId;

    // WRONG: overrides equals but not hashCode
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Account)) return false;
        return accountId.equals(((Account) o).accountId);
    }
    // Missing hashCode override!
}
JAVA
// This breaks HashMap lookups
Map<Account, String> map = new HashMap<>();
Account a1 = new Account("SAV2001");
map.put(a1, "Rajesh");
Account a2 = new Account("SAV2001");
System.out.println(map.get(a2));  // null! Different hashCode

Correct approach:

JAVA
package com.bank.model;

import java.util.Objects;

public class Account {
    private String accountId;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Account)) return false;
        return accountId.equals(((Account) o).accountId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(accountId);
    }
}

Now both a1 and a2 produce the same hash code, so HashMap lookups work correctly.

What are the edge cases?

Edge cases can cause unexpected behavior in banking applications. Here are some to watch for.

JAVA
package com.bank.edge;

public class EdgeCaseDemo {
    public static void main(String[] args) {
        // Edge case 1: null handling
        String accountId = null;
        try {
            System.out.println(accountId.length());
        } catch (NullPointerException e) {
            System.out.println("Null account ID");
        }

        // Edge case 2: empty vs null
        String emptyId = "";
        System.out.println(emptyId.isEmpty());  // true
        System.out.println(emptyId == null);     // false

        // Edge case 3: boundary values
        java.math.BigDecimal zero = java.math.BigDecimal.ZERO;
        java.math.BigDecimal negativeBalance = new java.math.BigDecimal("-0.01");
        System.out.println(zero.compareTo(negativeBalance) > 0);  // true
    }
}
TEXT
Null account ID
true
false
true

Always test with null values, empty strings, boundary values, and negative amounts in banking applications.

What are common interview follow-up questions?

Interviewers often follow up with these related questions:

  • How does this concept apply to a high-throughput banking transaction system?
  • What are the performance implications in a production environment?
  • How would you debug issues related to this concept?
  • What tools would you use to monitor this in production?
  • How does this behave differently in Java 8 vs Java 17+?

When answering follow-up questions, always relate your answer back to practical experience. For example, at National Bank of India, Rajesh debugged a production issue related to this concept by using JFR recordings and heap dumps. Providing concrete examples from banking systems shows the interviewer that you understand both the theory and the practice.

Visual Overview

DIAGRAM
  hashCode = constant:  all objects → one bucket → O(n) scan
  hashCode = well-distributed: objects spread evenly → O(1) average

  Java 8+: chain > 8 entries → tree → O(log n) worst case
  But O(log n) is still much worse than O(1)

Key Takeaway

A constant hashCode is legal but destroys HashMap performance. All objects land in one bucket, turning O(1) lookups into O(n) scans. A good hashCode distributes objects evenly across buckets.

One sentence to remember: a constant hashCode is legal but O(n) – a good hashCode distributes evenly for O(1).

---

Share
Use ← → arrow keys to navigate