Question 9 · Section 10

If Two Objects Have the Same hashCode(), Are They Necessarily Equal by equals()?

HashMap handles collisions — it stores multiple elements in one bucket and distinguishes them via equals().

Language versions: English Russian Ukrainian

🟢 Junior Level

No! Two objects with the same hashCode() are not necessarily equal by equals(). This is called a collision — and it’s a normal situation.

Simple analogy: Two people may live in the same building entrance (same “hash”), but they are different people (not equal by “equals”).

Classic example:

System.out.println("Aa".hashCode());       // 2112
System.out.println("BB".hashCode());       // 2112
System.out.println("Aa".equals("BB"));     // false — different strings!

HashMap handles collisions — it stores multiple elements in one bucket and distinguishes them via equals().

🟡 Middle Level

Why Collisions Are Inevitable

hashCode() returns an int — 32 bits, ~4.3 billion values. But there can be more unique objects than fit in JVM memory. Even strings of up to 10 characters: 26^10 = 141 trillion combinations. By the pigeonhole principle, collisions are inevitable.

Two-Stage Filter in HashMap

Step Method Purpose
1. Coarse filter hashCode() Determines bucket, filters out 99.9%
2. Precise check equals() Finds specific key in the bucket

Why HashMap is faster: the two-stage filter is unique to hash collections. In ArrayList, search is linear equals() for EVERY element (O(n)). In TreeMap — compareTo() for each node (O(log n)). In HashMap — hashCode first discards 99.9% of candidates, and equals() is called on only 1-2 elements.

Performance:

  • Different hashCode → equals() not called (fast)
  • Same hashCode → equals() called (slower, but precise)

Why hashCode Uniqueness Matters

If all keys have the same hashCode:

  • All elements in one bucket
  • HashMap degrades to O(n) (list) or O(log n) (tree)
  • System becomes vulnerable to DoS attacks

Practical Collision Examples

// Strings
"Aa".hashCode() == "BB".hashCode() // 2112

// Real collision:
new Long(1).hashCode() == 1
new Long(4294967297L).hashCode() == 1  // Same hashCode!

// Bad hashCode
class BadKey {
    @Override public int hashCode() { return 1; } // ALWAYS 1!
}

🔴 Senior Level

Collision Mathematics

Birthday Paradox

Birthday paradox: in a group of 23 people, the probability of shared birthdays > 50%. Similarly in HashMap: even with 5 elements and 16 buckets, collision probability > 50%. This is counter-intuitive, but statistically proven.

Performance Impact

Scenario Average Chain Length Complexity
Good hashCode 1-2 O(1)
Medium hashCode 3-5 O(1) (with large constant)
Bad hashCode (constant) n O(n) or O(log n)

Hash Flooding Attack

Attacker crafts keys with the same hash:

  • Java 7: 100% CPU on insertion (O(n²))
  • Java 8+: mitigated via treeification (O(n log n)), but overhead remains

Internal Mechanics: Treeification on Collisions

At 8+ elements in one bucket and capacity >= 64:

// If keys implement Comparable — compareTo() is used
// If not — tieBreakOrder() via System.identityHashCode()

Implementing Comparable for keys speeds up treeification.

Edge Cases

  1. All hashCode = 1: In Java 7 — fatal degradation. In Java 8+ — tree, but huge overhead
  2. hashCode depends on mutable fields: after key modification, hash changes, element is “lost”
  3. System hashCode: System.identityHashCode() may coincide for different objects (it’s not a UUID)

Best Practices

  • Minimize collisions through quality hashCode()
  • Use prime numbers (31, 37, 41) as multipliers
  • Include all significant fields from equals() in hashCode()
  • For production: monitor hashCode distribution via unit tests

🎯 Interview Cheat Sheet

Must know:

  • NO: same hashCode ≠ equal objects — this is called a collision
  • Classic example: “Aa” and “BB” both give 2112, but “Aa”.equals(“BB”) = false
  • HashMap uses a two-stage filter: hashCode (coarse filter) → equals (precise check)
  • Birthday paradox: with 23 elements and 365 buckets, collision probability > 50%
  • 2^32 (~4.3 billion) int values don’t cover the infinite set of possible objects
  • Bad hashCode (constant) = all elements in one bucket = degradation to O(n)

Common follow-up questions:

  • Why are collisions inevitable? — pigeonhole principle: infinite set → finite int range
  • How does HashMap distinguish elements in the same bucket? — via equals() along the chain/tree
  • What is a Hash Flooding Attack? — attacker generates keys with the same hashCode → DoS
  • Can collisions be completely avoided? — no, only minimized with quality hashCode

Red flags (DO NOT say):

  • “Collision = bug in hashCode” — no, collisions are normal, bug = ALWAYS returning a constant
  • “On collision HashMap loses data” — no, equals finds the element inside the bucket
  • “hashCode must be unique” — mathematically impossible for an infinite set

Related topics:

  • [[04. What is a Collision in HashMap]]
  • [[05. How HashMap Handles Collisions]]
  • [[07. What is the equals() and hashCode() Contract]]
  • [[08. If Two Objects Are Equal by equals(), What Can You Say About Their hashCode()]]