Feedback Form

Custom Objects in HashSet: equals(), hashCode(), and Immutability Rules

Custom Objects in HashSet: equals(), hashCode(), and Immutability Rules

Custom Objects in HashSet क्या होते हैं?

जब हम HashSet में simple data जैसे Integer, String या Double store करते हैं, तो Java खुद ही उनकी comparison और uniqueness handle कर लेता है। लेकिन जब हम अपने बनाए हुए Custom Objects (जैसे Student, Employee, Book आदि) को HashSet में डालते हैं, तो Java को यह समझाने के लिए हमें बताना पड़ता है कि दो objects equal हैं या नहीं। यहीं पर equals() और hashCode() methods की importance शुरू होती है।

HashSet की working समझो simple words में

HashSet अंदर से HashMap का use करता है। जब भी कोई new object add करते हो, HashSet पहले उस object का hashCode() निकालता है और फिर उसी के base पर bucket decide करता है। अगर उसी bucket में कोई और object पहले से मौजूद है, तो फिर Java equals() method call करके check करता है कि दोनों objects एक जैसे हैं या नहीं। अगर equal हैं, तो duplicate नहीं डाला जाता; अगर अलग हैं, तो नया object insert हो जाता है।

Example:

class Student {
  int id;
  String name;

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

public class Main {
  public static void main(String[] args) {
    HashSet<Student> set = new HashSet<>();
    set.add(new Student(101, "Aman"));
    set.add(new Student(101, "Aman"));
    System.out.println(set.size());
  }
}

इस code का output क्या होगा? — 2. क्योंकि Java को यह नहीं पता कि दोनों objects logically same हैं। Default equals() और hashCode() सिर्फ object reference compare करते हैं।

equals() method क्या करता है?

equals() method यह define करता है कि दो objects logically equal हैं या नहीं। अगर हम चाहते हैं कि HashSet हमारे custom objects को logically compare करे, तो हमें equals() override करना होगा।

Example of overriding equals():

@Override
public boolean equals(Object obj) {
  if (this == obj)
    return true;
  if (obj == null || getClass() != obj.getClass())
    return false;
  Student s = (Student) obj;
  return id == s.id && name.equals(s.name);
}

अब हमारा equals() method यह check करेगा कि अगर id और name दोनों same हैं, तो objects same माने जाएं।

hashCode() method क्यों जरूरी है?

अगर आप equals() override करते हैं तो hashCode() override करना भी जरूरी है। क्योंकि HashSet object को buckets में store करने के लिए hashCode() का use करता है। अगर logically equal objects का hash code अलग हुआ, तो HashSet उन्हें duplicate नहीं मानेगा और अलग-अलग store कर देगा।

Example of overriding hashCode():

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

अब hashCode() और equals() दोनों synchronized हैं — यानी अगर दो students की id और name same हैं, तो उनका hash code भी same होगा और HashSet उन्हें duplicate नहीं डालेगा।

Modified Example:

class Student {
  int id;
  String name;

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

  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null || getClass() != obj.getClass()) return false;
    Student s = (Student) obj;
    return id == s.id && name.equals(s.name);
  }

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

public class Main {
  public static void main(String[] args) {
    HashSet<Student> set = new HashSet<>();
    set.add(new Student(101, "Aman"));
    set.add(new Student(101, "Aman"));
    System.out.println(set.size()); // Output: 1
  }
}

अब HashSet समझेगा कि दोनों objects logically same हैं और duplicate entry नहीं डालेगा।

equals() और hashCode() के बीच संबंध

ConditionResult
अगर दो objects equal हैंतो उनके hashCode भी same होने चाहिए
अगर दो objects के hashCode same हैंतो जरूरी नहीं कि दोनों equal हों
अगर hashCode अलग हैंतो objects कभी equal नहीं होंगे

यह rule Java के contract में defined है और HashSet इन्हीं पर काम करता है। अगर यह rule follow नहीं किया गया तो HashSet unpredictable behavior दिखा सकता है।

Immutability Rule in HashSet

Immutability का मतलब होता है — object की state (values) add होने के बाद change नहीं होनी चाहिए। HashSet में जब कोई object add होता है, तो उसका hashCode() calculate करके bucket में डाल दिया जाता है। अगर बाद में आपने object की field change कर दी, तो उसका hash code भी बदल जाएगा, लेकिन HashSet को इसका पता नहीं चलेगा। Result — वो object HashSet में corrupt entry बन सकता है।

Example:

Student s1 = new Student(101, "Aman");
HashSet<Student> set = new HashSet<>();
set.add(s1);
System.out.println(set.contains(s1)); // true

s1.name = "Rahul"; // changing mutable field
System.out.println(set.contains(s1)); // false (unexpected result!)

यहाँ second contains() call false देगा क्योंकि object का hashCode() बदल गया है। इसलिए HashSet में add किए गए objects हमेशा immutable होने चाहिए

Immutability सुनिश्चित करने के तरीके:

  • Fields को final बनाओ।
  • Setter methods मत दो।
  • Constructor के ज़रिए ही values assign करो।
  • अगर object में collection हो, तो उसे भी unmodifiable बनाओ।

Custom Objects in HashSet के लिए Best Practices

  • equals() और hashCode() दोनों को हमेशा override करो।
  • दोनों methods का logic consistent रखो।
  • Immutable objects का use करो ताकि HashSet में corruption न हो।
  • Use Objects.equals() और Objects.hash() जैसे methods for simplicity।
  • अगर Object identity important है, तो IdentityHashMap या LinkedHashSet use करो।

Real-Life Example: Employee Database

मान लो आपके पास Employee class है जिसमें empId unique identifier है। आप चाहते हो कि HashSet में एक ही employee की duplicate entry न आए। तब आप केवल empId को comparison के लिए use कर सकते हो।

class Employee {
  final int empId;
  final String name;

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

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

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

अब चाहे नाम बदल जाए, अगर empId same है तो HashSet duplicate entry नहीं डालेगा। यह approach database-like behavior देती है।

Common Mistakes जो students अक्सर करते हैं

  • सिर्फ equals() override करना और hashCode() भूल जाना।
  • Mutable fields को comparison में include करना।
  • String comparison के लिए == operator use करना।
  • Class में null safety handle न करना।
  • hashCode logic inconsistent रखना जिससे duplicate entries आ जाएं।

Exam-Oriented Notes (Quick Revision)

ConceptExplanation
Custom ObjectUser-defined class जैसे Student या Employee जिसे HashSet में add करते हैं
equals()दो objects logically equal हैं या नहीं यह बताता है
hashCode()HashSet में object की storage location तय करता है
Contract RuleEqual objects का hashCode same होना चाहिए
ImmutabilityObjects की state add होने के बाद नहीं बदलनी चाहिए
Duplicate Controlequals() और hashCode() का सही implementation duplicate रोकता है
Best PracticeAlways override both methods + keep objects immutable

Important Points याद रखने के लिए

  • HashSet duplicate objects allow नहीं करता — लेकिन equality check developer के हाथ में है।
  • equals() और hashCode() दोनों override करना जरूरी है।
  • Equal objects का hash code हमेशा same होना चाहिए।
  • Immutable objects HashSet के लिए सबसे सुरक्षित होते हैं।
  • Inconsistent hashCode और mutable fields से HashSet corrupt हो सकता है।

Key Takeaway

अगर आप चाहते हो कि आपका HashSet सही तरीके से custom objects handle करे, तो हमेशा equals() और hashCode() को properly override करो और objects को immutable रखो। यही तीन rules — equals(), hashCode(), और Immutability — HashSet में custom objects को reliable बनाते हैं।