যখন আপনি equals() override করে যুক্তিসংগত সমতা সংজ্ঞায়িত করেন, তখন আপনাকে অবশ্যই hashCode() ও override করতে হবে — কারণ hash-ভিত্তিক সংগ্রহ (HashMap, ) এই চুক্তির উপর নির্ভর করে যে । এটি ভঙ্গ করলে সূক্ষ্ম, খুঁজে পাওয়া কঠিন বাগ হয়।
যখন আপনি equals() override করে যুক্তিসংগত সমতা সংজ্ঞায়িত করেন, তখন আপনাকে অবশ্যই hashCode() ও override করতে হবে — কারণ hash-ভিত্তিক সংগ্রহ (HashMap, ) এই চুক্তির উপর নির্ভর করে যে । এটি ভঙ্গ করলে সূক্ষ্ম, খুঁজে পাওয়া কঠিন বাগ হয়।
HashSet// by default, equals() compares IDENTITY (same object?), hashCode() is based on memory address
Person p1 = new Person("Ann", 30);
Person p2 = new Person("Ann", 30);
p1.equals(p2); // false by default — different objects, even with same data
Override না করে, একই বিষয়বস্তু সহ দুটি বস্তু "সমান নয়" — সাধারণত value বস্তুর জন্য আপনি যা চান তা নয়।
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person p = (Person) o;
return age == p.age && Objects.equals(name, p.name); // compare by CONTENT
}
@Override
public int hashCode() {
return Objects.hash(name, age); // MUST be consistent with equals()
}
চুক্তি: যদি a.equals(b) সত্য হয়, তাহলে a.hashCode() == b.hashCode() অবশ্যই সত্য হতে হবে।
Person p1 = new Person("Ann", 30);
Person p2 = new Person("Ann", 30); // equal by our equals()
Set<Person> set = new HashSet<>();
set.add(p1);
set.contains(p2); // ❌ likely FALSE — even though p1.equals(p2)!
কেন? HashMap/HashSet প্রথমে সঠিক bucket খুঁজে পেতে hashCode() ব্যবহার করে, তারপর এর ভিতরে equals() ব্যবহার করে। ডিফল্ট (পরিচয়-ভিত্তিক) hashCode() দিয়ে, p1 এবং p2 বিভিন্ন bucket এ পড়ে, তাই contains কখনও তাদের তুলনা করে না — সেট মনে করে তারা আলাদা। এটি বিভ্রান্তিকর বাগ তৈরি করে: একটি Set-এ সদৃশ, ব্যর্থ map লুকআপ, ইত্যাদি।
1. equal objects → equal hash codes (REQUIRED for correctness)
2. unequal objects MAY have the same hash (collisions are allowed)
3. hashCode() must be consistent (same object → same code, unchanged)
4. equals() must be reflexive, symmetric, transitive, consistent
record Person(String name, int age) {} // records auto-generate equals/hashCode/toString!
Java records (এবং IDE প্রজন্ম / Lombok) আপনার জন্য সঠিক, সামঞ্জস্যপূর্ণ equals/hashCode তৈরি করে।
equals/hashCode চুক্তি Java-এর সবচেয়ে গুরুত্বপূর্ণ — এবং সবচেয়ে সাধারণ লঙ্ঘন — নিয়মগুলির মধ্যে একটি।
equals() override করা কিন্তু hashCode() না করা hash-ভিত্তিক সংগ্রহ ভাঙ্গে এমনভাবে যা ডিবাগ করা কঠিন: যে বস্তুগুলি আপনি সমান মনে করেন সেগুলি HashMap/HashSet-এ হারিয়ে যায় (ব্যর্থ লুকআপ, ফ্যান্টম সদৃশ) কারণ তারা বিভিন্ন bucket এ hash করে।
যেহেতু এই সংগ্রহগুলি সর্বত্র বিদ্যমান, প্রতিটি value-type শ্রেণী যা একটি কী বা সেট উপাদান হিসাবে ব্যবহৃত হয় তাকে উভয়ই সামঞ্জস্যপূর্ণভাবে override করতে হবে।
কেন বুঝা (bucket-তারপর-সমান লুকআপ মেকানিজম) — এবং এটি সঠিকভাবে পেতে records বা উত্পন্ন কোড ব্যবহার করা — সঠিক আচরণের জন্য অপরিহার্য এবং একটি ক্লাসিক সাক্ষাত্কার বিষয় যা Java সংগ্রহগুলি কীভাবে কাজ করে তার সত্যিকারের বোঝা প্রকাশ করে।