HashCode Method

Introduction

hashCode() returns an integer hash value for an object. Used in hash-based collections like HashMap, HashSet.


Default Implementation

class Student {
    String name;
    int rollNo;

    Student(String name, int rollNo) {
        this.name = name;
        this.rollNo = rollNo;
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("John", 101);
        Student s2 = new Student("John", 101);

        // Default hashCode() returns different values
        System.out.println(s1.hashCode());  // e.g., 366712642
        System.out.println(s2.hashCode());  // e.g., 1829164700
    }
}

Why Override hashCode()?

For hash-based collections to work correctly.

import java.util.HashSet;

class Student {
    String name;
    int rollNo;

    Student(String name, int rollNo) {
        this.name = name;
        this.rollNo = rollNo;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;

        Student s = (Student) obj;
        return rollNo == s.rollNo && name.equals(s.name);
    }

    // Without hashCode() override
}

public class Main {
    public static void main(String[] args) {
        HashSet<Student> set = new HashSet<>();

        Student s1 = new Student("John", 101);
        Student s2 = new Student("John", 101);

        set.add(s1);
        set.add(s2);

        // Without hashCode(), both added (wrong!)
        System.out.println(set.size());  // 2 (should be 1)
    }
}

Overriding hashCode()

class Student {
    String name;
    int rollNo;

    Student(String name, int rollNo) {
        this.name = name;
        this.rollNo = rollNo;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;

        Student s = (Student) obj;
        return rollNo == s.rollNo && name.equals(s.name);
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + rollNo;
        return result;
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("John", 101);
        Student s2 = new Student("John", 101);

        System.out.println(s1.hashCode());  // Same
        System.out.println(s2.hashCode());  // Same
        System.out.println(s1.equals(s2));  // true
    }
}

hashCode() Contract

3 Rules:

  1. Consistent: Same object → same hash (if not modified)
  2. Equals → Same hash: If x.equals(y) is true, then x.hashCode() == y.hashCode()
  3. Not required reverse: Different objects CAN have same hash (collision allowed)
// Rule 1: Consistent
Student s = new Student("John", 101);
int hash1 = s.hashCode();
int hash2 = s.hashCode();
// hash1 == hash2 (must be same)

// Rule 2: equals → same hash
Student s1 = new Student("John", 101);
Student s2 = new Student("John", 101);
if (s1.equals(s2)) {
    // s1.hashCode() == s2.hashCode() (must be true)
}

// Rule 3: Different objects can have same hash
Student s3 = new Student("Alice", 102);
Student s4 = new Student("Bob", 103);
// s3.hashCode() might equal s4.hashCode() (collision OK)

Simple hashCode() Formula

@Override
public int hashCode() {
    int result = field1.hashCode();           // First field
    result = 31 * result + field2.hashCode(); // Second field
    result = 31 * result + primitiveField;    // Primitive field
    return result;
}

Why 31?

  • Prime number
  • Good distribution
  • Efficient: 31 * x = (x << 5) - x

Examples for Different Types

Single Field:

class Person {
    String name;

    @Override
    public int hashCode() {
        return name.hashCode();
    }
}

Multiple Fields:

class Person {
    String name;
    int age;

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }
}

With null Check:

class Person {
    String name;
    String city;

    @Override
    public int hashCode() {
        int result = (name != null) ? name.hashCode() : 0;
        result = 31 * result + ((city != null) ? city.hashCode() : 0);
        return result;
    }
}

Complete Example

import java.util.HashSet;

class Employee {
    private String empId;
    private String name;
    private double salary;

    Employee(String empId, String name, double salary) {
        this.empId = empId;
        this.name = name;
        this.salary = salary;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;

        Employee emp = (Employee) obj;
        return empId.equals(emp.empId);  // Compare by ID only
    }

    @Override
    public int hashCode() {
        return empId.hashCode();  // Hash based on ID only
    }

    @Override
    public String toString() {
        return "Employee[" + empId + ", " + name + ", $" + salary + "]";
    }
}

public class Main {
    public static void main(String[] args) {
        HashSet<Employee> employees = new HashSet<>();

        Employee e1 = new Employee("E001", "John", 50000);
        Employee e2 = new Employee("E001", "John Updated", 55000);
        Employee e3 = new Employee("E002", "Alice", 60000);

        employees.add(e1);
        employees.add(e2);  // Same ID as e1, not added
        employees.add(e3);

        System.out.println("Set size: " + employees.size());  // 2
        System.out.println("Contains e2: " + employees.contains(e2));  // true

        for (Employee emp : employees) {
            System.out.println(emp);
        }
    }
}

Using Objects.hash() (Java 7+)

Easier way to generate hashCode().

import java.util.Objects;

class Person {
    String name;
    int age;
    String city;

    @Override
    public int hashCode() {
        return Objects.hash(name, age, city);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;

        Person person = (Person) obj;
        return age == person.age &&
               Objects.equals(name, person.name) &&
               Objects.equals(city, person.city);
    }
}

HashMap Example

import java.util.HashMap;

class Student {
    String name;
    int rollNo;

    Student(String name, int rollNo) {
        this.name = name;
        this.rollNo = rollNo;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;

        Student s = (Student) obj;
        return rollNo == s.rollNo && name.equals(s.name);
    }

    @Override
    public int hashCode() {
        return 31 * name.hashCode() + rollNo;
    }

    @Override
    public String toString() {
        return name + "(" + rollNo + ")";
    }
}

public class Main {
    public static void main(String[] args) {
        HashMap<Student, String> grades = new HashMap<>();

        Student s1 = new Student("John", 101);
        Student s2 = new Student("Alice", 102);

        grades.put(s1, "A");
        grades.put(s2, "B");

        // Lookup with equal object
        Student s3 = new Student("John", 101);  // Equal to s1
        System.out.println("Grade: " + grades.get(s3));  // A
    }
}

Common Mistakes

Mistake 1: Override equals() but not hashCode()

class Student {
    String name;
    int rollNo;

    @Override
    public boolean equals(Object obj) {
        // ... implementation
    }

    // ✗ Missing hashCode() - breaks HashMap/HashSet
}

Mistake 2: Using Mutable Fields

class Student {
    String name;  // Mutable

    @Override
    public int hashCode() {
        return name.hashCode();
    }
}

HashSet<Student> set = new HashSet<>();
Student s = new Student("John");
set.add(s);

s.name = "Alice";  // ✗ Hash changed, object lost in set!
System.out.println(set.contains(s));  // false (can't find it!)

Mistake 3: Inconsistent with equals()

class Student {
    String name;
    int rollNo;

    @Override
    public boolean equals(Object obj) {
        // Compares name and rollNo
    }

    @Override
    public int hashCode() {
        return name.hashCode();  // ✗ Only uses name
    }
}

Hash Collision

Different objects with same hash code.

class Point {
    int x, y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public int hashCode() {
        return x + y;  // Simple hash
    }
}

public class Main {
    public static void main(String[] args) {
        Point p1 = new Point(1, 2);
        Point p2 = new Point(2, 1);

        // Collision: same hash, different objects
        System.out.println(p1.hashCode());  // 3
        System.out.println(p2.hashCode());  // 3
        System.out.println(p1.equals(p2));  // false
    }
}

HashSet Internal Working

// When you do: set.add(object)

// Step 1: Calculate hash
int hash = object.hashCode();

// Step 2: Find bucket
int bucket = hash % bucketCount;

// Step 3: Check if exists in bucket
for (Object obj : buckets[bucket]) {
    if (obj.equals(object)) {
        return false;  // Already exists
    }
}

// Step 4: Add to bucket
buckets[bucket].add(object);
return true;

Performance Impact

Bad hashCode() - All Same:

@Override
public int hashCode() {
    return 1;  // ✗ All objects in one bucket - slow!
}

Good hashCode() - Well Distributed:

@Override
public int hashCode() {
    return Objects.hash(field1, field2, field3);
}

Inheritance and hashCode()

class Person {
    String name;
    int age;

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

class Employee extends Person {
    String empId;

    @Override
    public int hashCode() {
        int result = super.hashCode();  // Include parent fields
        result = 31 * result + empId.hashCode();
        return result;
    }
}

Testing hashCode()

class Student {
    String name;
    int rollNo;

    Student(String name, int rollNo) {
        this.name = name;
        this.rollNo = rollNo;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;

        Student s = (Student) obj;
        return rollNo == s.rollNo && name.equals(s.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, rollNo);
    }
}

public class Main {
    public static void main(String[] args) {
        Student s1 = new Student("John", 101);
        Student s2 = new Student("John", 101);
        Student s3 = new Student("Alice", 102);

        // Test: equal objects → same hash
        System.out.println("s1.equals(s2): " + s1.equals(s2));  // true
        System.out.println("s1.hashCode() == s2.hashCode(): " +
                          (s1.hashCode() == s2.hashCode()));    // true

        // Test: different objects → possibly different hash
        System.out.println("s1.equals(s3): " + s1.equals(s3));  // false
        System.out.println("s1.hashCode() == s3.hashCode(): " +
                          (s1.hashCode() == s3.hashCode()));    // likely false

        // Test: consistency
        int hash1 = s1.hashCode();
        int hash2 = s1.hashCode();
        System.out.println("Consistent: " + (hash1 == hash2));  // true
    }
}

Template for hashCode()

@Override
public int hashCode() {
    // Option 1: Using Objects.hash() (easy)
    return Objects.hash(field1, field2, field3);

    // Option 2: Manual calculation
    int result = field1.hashCode();
    result = 31 * result + field2.hashCode();
    result = 31 * result + primitiveField;
    return result;

    // Option 3: Single field
    return field.hashCode();
}

Quick Reference

import java.util.Objects;

class Example {
    String field1;
    int field2;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;

        Example e = (Example) obj;
        return field2 == e.field2 && Objects.equals(field1, e.field1);
    }

    @Override
    public int hashCode() {
        return Objects.hash(field1, field2);
    }
}

// Usage in collections
HashSet<Example> set = new HashSet<>();
HashMap<Example, String> map = new HashMap<>();

// Remember:
// - Always override with equals()
// - Use same fields as equals()
// - Keep consistent
// - Use Objects.hash() for simplicity

Exam Tips

Remember:

  1. hashCode() returns integer hash value
  2. Used in HashMap, HashSet
  3. Override with equals() (always together)
  4. Rule: equals() true → same hash
  5. Reverse not required: different objects can have same hash
  6. Use same fields as equals()
  7. Objects.hash() is easiest method
  8. 31 commonly used multiplier
  9. Consistent: same object → same hash
  10. Don’t use mutable fields

Common Questions:

  • What is hashCode()?
  • Why override hashCode()?
  • hashCode() contract?
  • Why override with equals()?
  • What happens if not overridden?
  • How to implement hashCode()?
  • What is hash collision?
  • Why use 31 in hashCode()?