۲۹ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

مقایسه اشیاء جاوا با ()quals و hashcode()

قرارداد بین ()quals و hashcode() چیست؟ یاد بگیرید که چگونه این روش ها هنگام مقایسه اشیاء جاوا با هم کار می کنند.

قرارداد بین ()quals و hashcode() چیست؟ یاد بگیرید که چگونه این روش ها هنگام مقایسه اشیاء جاوا با هم کار می کنند.

equals() و hashcode() جاوا دو روشی هستند که با هم کار می کنند تا بررسی کنند که آیا دو شی دارای مقدار یکسانی هستند یا خیر. می‌توانید از آنها برای مقایسه آسان و کارآمد در برنامه‌های جاوا استفاده کنید.

استفاده از برابر () و hashcode() در جاوا

در این مقاله، یاد خواهید گرفت:

  • چرا equals() و hashcode() را در جاوا لغو کنید؟
  • نحوه مقایسه اشیاء جاوا با equals()
  • نحوه شناسایی اشیاء جاوا با hashcode()
  • نحوه استفاده از equals() و hashcode() با مجموعه ها

همچنین دریافت خواهید کرد:

  • دستورالعمل‌هایی برای استفاده از equals() و hashcode()
  • قوانین مقایسه اشیا با equals() و hashcode()
  • اشتباهاتی که هنگام استفاده از equals() و hashcode()
  • اجتناب کنید

  • چه چیزی را در مورد equals() و hashcode()
  • به خاطر بسپارید

خط پایین: چرا از برابر () و hashcode() استفاده کنیم؟

بدون equals() و hashcode()، باید هر فیلد را از یک شی مقایسه کنیم. کد بسیار گیج کننده و خواندن آن سخت خواهد بود. استفاده از روش‌های equals() و hashcode() با هم منجر به کد انعطاف‌پذیرتر و منسجم‌تر می‌شود.

چرا override برابر با() و hashcode() در جاوا است؟

بازگردانی روش تکنیکی است که در آن رفتار کلاس یا رابط والد دوباره در زیر کلاس نوشته می‌شود (بازنویسی می‌شود) تا از مزیت Polymorphism استفاده شود. هر Object در جاوا شامل یک روش equals() و یک hashcode() است، اما برای درست کار کردن باید آنها را لغو کرد.

برای درک اینکه چگونه overriding با equals() و  hashcode() کار می‌کند، می‌توانیم اجرای آنها را در کلاس‌های اصلی جاوا مطالعه کنیم. در زیر متد equals() در کلاس Object آمده است. این روش بررسی می‌کند که آیا نمونه فعلی مشابه Object قبلی است یا خیر.


public boolean equals(Object obj) {
        return (this == obj);
}

وقتی روش hashcode() لغو نشود، روش پیش‌فرض در کلاس Object فراخوانی می‌شود. این یک روش بومی است، به این معنی که در زبان دیگری مانند C اجرا می‌شود و مقداری کد مربوط به آدرس حافظه شی را برمی‌گرداند. (این مهم نیست که دقیقاً بدانید این روش چگونه کار می کند مگر اینکه در حال نوشتن کد JDK باشید.)


@HotSpotIntrinsicCandidate
public native int hashCode();

وقتی روش‌های equals() و hashcode() لغو نمی‌شوند، به جای آن متدهای بالا را فراخوانی می‌کنید. در این مورد، متدها هدف واقعی equals() و hashcode() را برآورده نمی‌کنند، یعنی بررسی اینکه آیا دو یا چند شی دارای مقادیر یکسان هستند.< /p>

به عنوان یک قاعده، وقتی equals() را لغو می‌کنید، باید hashcode() را نیز لغو کنید.

کد را دریافت کنید

کد منبع چالش‌های جاوا را دریافت کنید.

نحوه مقایسه اشیاء با ()quals

ما از متد equals() برای مقایسه اشیاء در جاوا استفاده می کنیم. برای تعیین یکسان بودن دو شیء، equals() مقادیر ویژگی های اشیاء را با هم مقایسه می کند:


public class EqualsAndHashCodeExample {

    public static void main(String... equalsExplanation) {
        System.out.println(new Simpson("Homer", 35, 120)
                 .equals(new Simpson("Homer",35,120)));
        
        System.out.println(new Simpson("Bart", 10, 120)
                 .equals(new Simpson("El Barto", 10, 45)));
        
        System.out.println(new Simpson("Lisa", 54, 60)
                 .equals(new Object()));
    }
	
    static class Simpson {

        private String name;
        private int age;
        private int weight;

        public Simpson(String name, int age, int weight) {
            this.name = name;
            this.age = age;
            this.weight = weight;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            Simpson simpson = (Simpson) o;
            return age == simpson.age &&
                    weight == simpson.weight &&
                    name.equals(simpson.name);
        }
    }

}

در مقایسه اول، equals() نمونه شی فعلی را با شی ای که ارسال شده مقایسه می کند. اگر دو شی مقادیر یکسانی داشته باشند، equals() true را برمی‌گرداند.

در مقایسه دوم، equals() بررسی می کند که آیا شیء ارسال شده null است یا به عنوان کلاس دیگری تایپ شده است. اگر کلاس متفاوتی باشد، اشیاء برابر نیستند.

در نهایت، equals() فیلدهای اشیا را با هم مقایسه می کند. اگر دو شیء دارای مقادیر فیلد یکسانی باشند، آنگاه شیءها یکسان هستند.

مقایسه اشیا

اکنون، اجازه دهید نتایج این مقایسه‌ها را در روش main() خود مشاهده کنیم. ابتدا دو شی Simpson را با هم مقایسه می کنیم:


System.out.println(new Simpson("Homer", 35, 120).equals(new Simpson("Homer", 35, 120)));

اشیاء در اینجا یکسان هستند، بنابراین نتیجه true خواهد بود.

بعد، دوباره دو شی Simpson را با هم مقایسه می کنیم:


System.out.println(new Simpson("Bart", 10, 45).equals(new Simpson("El Barto", 10, 45))); 

اشیاء اینجا تقریباً یکسان هستند اما نام آنها متفاوت است: بارت و ال بارتو. بنابراین نتیجه نادرست خواهد بود.

در نهایت، بیایید یک شی Simpson و یک نمونه از کلاس شیء:


System.out.println(new Simpson("Lisa", 54, 60).equals(new Object())); 

در این مورد، نتیجه نادرست خواهد بود زیرا انواع کلاس‌ها متفاوت است.

equals() با ==

یکسان نیست

در نگاه اول، عملگر == و متد equals() ممکن است کار مشابهی را انجام دهند، اما متفاوت عمل می‌کنند. عملگر == مقایسه می کند که آیا دو مرجع شی به یک شی اشاره دارند یا خیر. به عنوان مثال:


System.out.println(homer == homer2);

در اولین مقایسه، ما دو نمونه مختلف Simpson را با استفاده از عملگر new نمونه‌سازی کردیم. به همین دلیل، متغیرهای homer و homer2 به مراجع مختلف Object در هپ حافظه. بنابراین ما false را به عنوان نتیجه خواهیم داشت.

System.out.println(homer.equals(homer2));

در مقایسه دوم، روش equals() را لغو می‌کنیم. در این مورد فقط نام ها مقایسه می شوند. از آنجایی که نام هر دو شی Simpson “Homer” است، نتیجه true است.

نحوه شناسایی اشیاء با hashcode()

ما از روش hashcode() برای بهینه سازی عملکرد هنگام مقایسه اشیا استفاده می کنیم. اجرای hashcode() یک شناسه منحصر به فرد برای هر شیء در برنامه شما برمی گرداند، که کار مقایسه کل وضعیت شی را بسیار آسان می کند.

اگر هش کد یک شی با هش کد شی دیگر یکسان نیست، دلیلی برای اجرای متد equals() وجود ندارد: فقط می دانید که این دو شی یکسان نیستند. از طرف دیگر، اگر کد هش یکسان است، باید متد equals() را اجرا کنید تا تعیین کنید که آیا مقادیر و فیلدها یکسان هستند یا نه.

در اینجا یک مثال عملی با hashcode() آورده شده است.


public class HashcodeConcept {

    public static void main(String... hashcodeExample) {
        Simpson homer = new Simpson(1, "Homer");
        Simpson bart = new Simpson(2, "Homer");

        boolean isHashcodeEquals = homer.hashCode() == bart.hashCode();

        if (isHashcodeEquals) {
            System.out.println("Should compare with equals method too.");
        } else {
            System.out.println("Should not compare with equals method because " +
                    "the id is different, that means the objects are not equals for sure.");
        }
    }

     static class Simpson {
        int id;
        String name;

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

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

        @Override
        public int hashCode() {
            return id;
        }
    }
}

یک hashcode() که همیشه همان مقدار را برمی‌گرداند معتبر است اما چندان مؤثر نیست. در این حالت مقایسه همیشه true را برمی‌گرداند، بنابراین روش equals() همیشه اجرا می‌شود. در این مورد هیچ بهبودی در عملکرد وجود ندارد.

نحوه استفاده از برابر () و hashcode() با مجموعه ها

رابط Set مسئول اطمینان از اینکه هیچ عنصر تکراری در زیر کلاس Set درج نمی‌شود، است. در زیر برخی از کلاس هایی هستند که رابط Set را پیاده سازی می کنند:

فقط عناصر منحصربه‌فرد را می‌توان در Set درج کرد، بنابراین اگر می‌خواهید عنصری را به کلاس HashSet اضافه کنید (به عنوان مثال)، ابتدا باید از < استفاده کنید code>equals() و روش‌های hashcode() برای تأیید اینکه عنصر منحصر به فرد است. اگر روش‌های equals() و hashcode() در این مورد نادیده گرفته نمی‌شوند، خطر درج عناصر تکراری در کد وجود دارد.

در کد زیر، از روش add برای افزودن یک عنصر جدید به یک شی HashSet استفاده می‌کنیم. قبل از اینکه عنصر جدید اضافه شود، HashSet بررسی می‌کند که آیا عنصر از قبل در مجموعه داده شده وجود دارد یا خیر:


if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
       break;
       p = e; 

اگر شیء یکسان باشد، عنصر جدید درج نخواهد شد.

مجموعه های هش

Set تنها مجموعه‌ای نیست که از equals() و hashcode() استفاده می‌کند. HashMap، Hashtable، و LinkedHashMap نیز به این روش ها نیاز دارد. به عنوان یک قاعده، اگر مجموعه‌ای را می‌بینید که پیشوند «Hash» دارد، می‌توانید مطمئن باشید که نیاز به لغو روش‌های hashcode() و equals() دارد. ویژگی های آنها به درستی کار کند.

دستورالعمل‌هایی برای استفاده از برابر () و hashcode()

شما فقط باید یک متد equals() را برای اشیایی که دارای شناسه هش کد منحصر به فرد یکسانی هستند اجرا کنید. وقتی شناسه کد هش متفاوت است، equals() را نباید اجرا کنید.

جدول ۱. مقایسه کدهای هش

را اجرا کنید

را اجرا نکنید

اگر مقایسه hashcode() سپس…
درست برمی‌گرداند equals()
نادرست را برمی گرداند equals()

این اصل عمدتاً در مجموعه‌های Set یا Hash به دلایل عملکرد استفاده می‌شود.

قوانین مقایسه شی با ()quals و hashcode()

وقتی مقایسه hashcode() false را برمی‌گرداند، روش equals() باید false را نیز برگرداند. اگر کد هش متفاوت باشد، قطعاً اشیاء برابر نیستند.

جدول ۲. مقایسه شیء با hashcode()

برگردد

وقتی مقایسه کد هش برمی گردد … متد equals() باید به …
درست درست یا نادرست
نادرست نادرست

وقتی روش equals() true را برمی‌گرداند، به این معنی است که اشیاء در همه مقادیر و ویژگی‌ها برابر هستند. در این مورد،  مقایسه کد هش نیز باید درست باشد.

جدول ۳. مقایسه شیء با برابر ()

را برمی گرداند

برگردد

وقتی متد equals() روش hashcode() باید …
درست درست
نادرست درست یا نادرست

چالش برابر () و hashcode() را انتخاب کنید!

وقت آن است که مهارت‌های خود را با equals() و hashcode() آزمایش کنید. هدف شما در این چالش این است که خروجی دو مقایسه روش equals() را مشخص کنید و اندازه مجموعه Set را حدس بزنید.

برای شروع، کد زیر را با دقت مطالعه کنید:


public class EqualsHashCodeChallenge {

    public static void main(String... doYourBest) {
        System.out.println(new Simpson("Bart").equals(new Simpson("Bart")));
        Simpson overriddenHomer = new Simpson("Homer") {
            public int hashCode() {
                return (43 + 777) + 1;
            }
        };

        System.out.println(new Simpson("Homer").equals(overriddenHomer));

        Set set = new HashSet(Set.of(new Simpson("Homer"), new Simpson("Marge")));
        set.add(new Simpson("Homer"));
        set.add(overriddenHomer);

        System.out.println(set.size());
    }

    static class Simpson {
        String name;

        Simpson(String name) {
            this.name = name;
        }

        @Override
        public boolean equals(Object obj) {
            Simpson otherSimpson = (Simpson) obj;
            return this.name.equals(otherSimpson.name) &&
                    this.hashCode() == otherSimpson.hashCode();
        }

        @Override
        public int hashCode() {
            return (43 + 777);
        }
    }

}

به خاطر داشته باشید، ابتدا کد را تجزیه و تحلیل کنید، نتیجه را حدس بزنید، سپس کد را اجرا کنید. هدف شما بهبود مهارت خود با تجزیه و تحلیل کد و جذب مفاهیم اصلی جاوا برای قدرتمندتر کردن کد شما است. قبل از بررسی پاسخ صحیح زیر، پاسخ خود را انتخاب کنید.

A)


true
true
۴

B)


true
false
۳ 

C)


true
false
۲

D)


false
true
۳ 

چه اتفاقی افتاد؟

در اولین مقایسه روش equals()، نتیجه true است زیرا وضعیت شی دقیقاً یکسان است و hashcode() مقدار یکسانی را برای هر دو شی برمی‌گرداند.

در مقایسه روش دوم equals()، روش hashcode() برای متغیر overridenHomer نادیده گرفته می شود. نام "Homer" برای هر دو شی Simpson است، اما روش hashcode() مقدار متفاوتی را برای overriddenHomer برمی‌گرداند. در این حالت، نتیجه نهایی از روش equals() false خواهد بود زیرا این روش شامل مقایسه با کد هش است.

ممکن است متوجه شوید که اندازه مجموعه برای نگهداری سه شی Simpson تنظیم شده است. بیایید این را با جزئیات بررسی کنیم.

اولین شیء در مجموعه خواهد بود به طور معمول درج می شود:


new Simpson("Homer");

شیء بعدی نیز به طور معمول درج خواهد شد، زیرا مقداری متفاوت از شیء قبلی دارد:


new Simpson("Marge");

در نهایت،  شئ Simpson زیر همان مقدار شیء اول را دارد. در این حالت شی درج نخواهد شد:


set.add(new Simpson("Homer"));

همانطور که می دانیم، شی overridenHomer از مقدار کد هش متفاوت از نمونه معمولی Simpson("Homer") استفاده می کند. به همین دلیل، این عنصر در مجموعه درج می شود:


overriddenHomer;

کلید پاسخ

پاسخ این چالش کننده جاوا B است. خروجی این خواهد بود:


true 
false 
۳ 

شاید به این مطالب علاقمند باشید