دو متد hashCode و equlas در جاوا برای چک کردن برابر بودن دو object به کار می روند . با override کردن این دو متد می توانیم مدل کار کردن این دو متد را شخصی سازی کنیم .

equals

متد equlas رفرنس های object را در JVM مقایسه میکند و درصورت یکی بودن رفرنس ، مقدار true را بر می گرداند .
به عنوان مثال می خواهیم متد equals را برای شیء City استفاده و override کنیم :
public class City {

    private String name;

    private String zipCode;

    public City(String name, String zipCode) {
        this.name = name;
        this.zipCode = zipCode;
    }

    public String getName() {
        return name;
    }

    public String getZipCode() {
        return zipCode;
    }
    
}​

این کلاس دارای دو فیلد بسیار ساده به نام name و zipCode است . حال میخواهیم دو instance مختلف از این کلاس را ساخته و equals بودن این دو را بررسی کنیم :

public class main {

    public static void main(String[] args) {
        City tehran1 = new City("Tehran", "1234");
        City tehran2 = new City("Tehran", "1234");
        System.out.printf("%s is equal to %s : %s",tehran1.getName(),tehran2.getName(),tehran1.equals(tehran2));
    }

}

خروجی این کد ، مقدار false می باشد :


Tehran is equal to Tehran : false

به دلیل اینکه کلاس tehran2 به یک object و آدرس حافظه جداگانه ای نسبت به tehran1 اشاره می کند و در حقیقت دو instance از این کلاس ایجاد شده اند ، equality این دوکلاس ، false می باشد ، علی رغم یکی بودن دیتای آنها .
حال اگر بخواهیم مانند مثال بالا ، City هایی که نام آنها یکی است ، equality آنها true باشد ،  می توانیم با override کردن متد equals در کلاس City ، کارکرد این متد را شخصی سازی کنیم :

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        City other = (City) obj;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }

بعد از override کردن متد equals بر روی کلاس City ، City هایی که دارای نام یکسان هستند ، مقدار equaltiy آنها true می شود، علی رغم اشاره کردن به رفرنس های جدا از هم :

public class main {
    
    public static void main(String[] args) {
        City shiraz1 = new City("Shiraz", "1002");
        City shiraz2 = new City("Shiraz", "1002");
        System.out.printf("%s is equal to %s : %s",shiraz1.getName(),shiraz2.getName(),shiraz1.equals(shiraz2));
    }
    
}


Shiraz is equal to Shiraz : true

hashCode

هش کد ،  یک عدد شناسه منحصر به فرد است که توسط JVM به object ها اختصاص داده می شود .  اگر دو شیء برابر باشند ، این دو شیء باید کد هش یکسان داشته باشند و کاربرد آن ، بهینه سازی استفاده در collection هایی مانند HashMsp , HashSet می باشد .
به عبارت ساده تر ، hashCode  یک عدد صحیح 32بیتی یکتا را برای هر object است ، که توسط یک الگوریتم هشینگ ایجاد شده است .
هرگاه در طول اجرای یک برنامه جاوا ، یک شیء بیش از یک بار در فراخوانی شود ، hashCode  باید به طور مداوم همان مقدار قبلی را برگرداند ، مشروط بر اینکه دیتای آن شیء تغییر پیدا نکند .
حال با یک مثال کلاس City را در یم HashMap استفاده می کنیم :
    public static void main(String[] args) {
        HashMap<City, Integer> cityHashMap = new HashMap<>();
        City city1 = new City("Shiraz", "5522");
        City city2 = new City("Shiraz", "5522");
        int population = 1500000;

        cityHashMap.put(city1, population);
        cityHashMap.put(city2, population);

        Iterator cityMapIterator = cityHashMap.keySet().iterator();
        while (cityMapIterator.hasNext()) {
            City city = (City) cityMapIterator.next();
            System.out.printf("city name is %s \n", city.getName());
            System.out.printf("hashCode is : %d \n",city.hashCode());
        }
    }

خروجی :


city name is Shiraz 
hashCode is : 1956725890 
city name is Shiraz 
hashCode is : 1163157884 

مدل کار HashMap به این صورت است که برای بازیابی اشیاء ، از hashCode های آنها استفاده می کند ، زیرا درون bucket خود ، hashCode تمامی object هایی که put شده اند را به عنوان یک key نگه می دارد و چون city1 و city2 دارای آدرس های حافظه متفاوتی هستند ، hashCode آنها نیز متفاوت است .
حال میخواهیم تمامی City هایی که دارای نام  و کد پستی یکسان هستند ، دارای hashCode یکسانی باشند  تا تنها ، یک instance از آنها در HashMap وجود داشته باشد .
توجه داشته باشید که عموما برای ایجاد hashCode ، باید از اعداد اول استفاده کنیم و همچنین هرچه الگوریتم هشی که برای محاسبه کدهای هش استفاده می کنیم بهتر باشد ، عملکرد جداول هش بهتر خواهد بود :

    @Override
    public int hashCode() {
        final int prime = 31;
        int hash = 7;
        hash = prime * hash + (int) name.length();
        hash = prime * hash + (name == null ? 0 : name.hashCode());
        hash = prime * hash + (zipCode == null ? 0 : zipCode.hashCode());
        return hash;
    }

حال اگر یک بار دیگر کد را اجرا کنیم ، خروجی کد به شکل زیر خواهد بود ، به دلیل یکی بودن hashCode کلاس های city1 و city2 ، تنها یک نمونه از آنها در سطل اشیای HashMap وجود خواهد داشت :


city name is Shiraz 
hashCode is : -566865464 

برای پیاده سازی متدهای hashCode و equals در جاوا ، کتابخانه هایی مانند lombok ، commons-lang وجود دارند ، به عنوان مثال اگر با استفاده از lombok بخواهیم این دو متد را پیاده سازی کنیم ، به روش زیر عمل میکنیم :
نصب lombok از طریق maven 

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

بعد از نصب lombok می توان از انوتیشن @EqualsAndHashCode ، بر روی کلاس های مورد نظر استفاده کرد :

import lombok.*;

@ToString
@EqualsAndHashCode
public class Country {
    private String name;
    private String capital;

    public Country(String name, String capital) {
        this.name = name;
        this.capital = capital;
    }

    public String getName() {
        return name;
    }

    public String getCapital() {
        return capital;
    }
}

public class main {

    public static void main(String[] args) {
        HashMap<Country, Integer> countryHashMap = new HashMap<>();
        int areaCode1 = 98;
        int areaCode2 = 1;

        Country country1 = new Country("Iran", "Tehran");
        Country country2 = new Country("Iran", "Tehran");

        Country country3 = new Country("Canada", "Ottawa");
        Country country4 = new Country("Canada", "Ottawa");

        countryHashMap.put(country1,areaCode1);
        countryHashMap.put(country2,areaCode1);

        countryHashMap.put(country3,areaCode2);
        countryHashMap.put(country4,areaCode2);

        Iterator countryMapIterator = countryHashMap.keySet().iterator();
        while (countryMapIterator.hasNext()) {
            Country country = (Country) countryMapIterator.next();
            System.out.println(country.toString());
            System.out.printf("hashCode is : %d \n", country.hashCode());
        }
    }

}

خروجی :


Country(name=Iran, capital=Tehran)
hashCode is : -1658671149 
Country(name=Canada, capital=Ottawa)
hashCode is : 768679919 

موضوعی که همیشه در هنگام override کردن متد های equals و hashCode برای کارایی بهتر ،  باید در نظر گرفته شود این است که همیشه این دو متد باید همراه با یکدیگر override شوند و نباید یکی از این دو متد به تنهایی override شود .
همچنین تولید پیاده سازی های کارآمد hashCode  اغلب به ترکیبی از چند مفهوم ریاضی ، (یعنی اعداد اول) ، عملیات ریاضی پایه نیاز دارد.