CodeGym /وبلاگ جاوا /Random-FA /روش برابر در جاوا: بهترین روش ها
John Squirrels
مرحله
San Francisco

روش برابر در جاوا: بهترین روش ها

در گروه منتشر شد
سلام! امروز در مورد دو روش مهم در جاوا صحبت خواهیم کرد: ()quals و hashCode() . این اولین باری نیست که ما آنها را ملاقات می کنیم: دوره CodeGym با یک درس کوتاه در مورد برابر () شروع می شود - اگر آن را فراموش کرده اید یا قبلاً آن را ندیده اید، آن را بخوانید... روش های برابر و هش کد: بهترین روش ها - 1در درس امروز، ما در مورد این مفاهیم به تفصیل صحبت خواهم کرد. و باور کنید، ما چیزی برای صحبت داریم! اما قبل از اینکه به مورد جدید برویم، بیایید مطالبی را که قبلاً پوشش داده‌ایم بازخوانی کنیم :) همانطور که به یاد دارید، مقایسه دو شیء با استفاده از عملگر == معمولاً ایده بدی است، زیرا == مراجع را مقایسه می‌کند. در اینجا مثال ما با اتومبیل ها از یک درس اخیر است:
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car car1 = new Car();
       car1.model = "Ferrari";
       car1.maxSpeed = 300;

       Car car2 = new Car();
       car2.model = "Ferrari";
       car2.maxSpeed = 300;

       System.out.println(car1 == car2);
   }
}
خروجی کنسول:

false
به نظر می‌رسد که ما دو شیء مشابه Car ایجاد کرده‌ایم : مقادیر فیلدهای مربوط به دو شیء ماشین یکسان است، اما نتیجه مقایسه هنوز نادرست است. ما قبلاً دلیل آن را می دانیم: ارجاعات car1 و car2 به آدرس های حافظه متفاوتی اشاره می کنند، بنابراین آنها با هم برابر نیستند. اما ما همچنان می خواهیم این دو شی را مقایسه کنیم، نه دو مرجع. بهترین راه حل برای مقایسه اشیا، متد ()quals است .

متد ()quals

ممکن است به خاطر داشته باشید که ما این متد را از ابتدا ایجاد نمی کنیم، بلکه آن را لغو می کنیم: متد () quals در کلاس Object تعریف شده است . گفته می شود، در شکل معمول خود، کاربرد کمی دارد:
public boolean equals(Object obj) {
   return (this == obj);
}
به این ترتیب متد () quals در کلاس Object تعریف می شود . این یک بار دیگر مقایسه مراجع است. چرا اینطوری ساختند؟ خوب، سازندگان زبان از کجا می دانند که کدام اشیاء در برنامه شما برابر و کدام یک نیستند؟ :) این نکته اصلی متد ()quals است - خالق یک کلاس کسی است که تعیین می کند در هنگام بررسی برابری اشیاء کلاس از کدام ویژگی استفاده شود. سپس متد () quals را در کلاس خود نادیده می گیرید. اگر دقیقاً معنی "تعیین کننده کدام ویژگی ها" را نمی فهمید، بیایید مثالی را در نظر بگیریم. در اینجا یک کلاس ساده به نمایندگی از یک مرد است: Man.
public class Man {

   private String noseSize;
   private String eyesColor;
   private String haircut;
   private boolean scars;
   private int dnaCode;

public Man(String noseSize, String eyesColor, String haircut, boolean scars, int dnaCode) {
   this.noseSize = noseSize;
   this.eyesColor = eyesColor;
   this.haircut = haircut;
   this.scars = scars;
   this.dnaCode = dnaCode;
}

   // Getters, setters, etc.
}
فرض کنید ما در حال نوشتن برنامه ای هستیم که باید مشخص کند که آیا دو نفر دوقلوهای همسان هستند یا شبیه به هم هستند. ما پنج ویژگی داریم: اندازه بینی، رنگ چشم، مدل مو، وجود جای زخم و نتایج آزمایش DNA (برای سادگی، این را به عنوان یک کد عدد صحیح نشان می‌دهیم). به نظر شما کدام یک از این ویژگی ها به برنامه ما اجازه می دهد تا دوقلوهای همسان را شناسایی کند؟ روش های برابر و هش کد: بهترین شیوه ها - 2البته فقط آزمایش DNA می تواند تضمین کند. دو نفر می توانند رنگ چشم، مدل مو، بینی و حتی زخم های یکسانی داشته باشند - افراد زیادی در جهان وجود دارند و نمی توان تضمین کرد که هیچ دوپلگانگر در آنجا وجود ندارد. اما ما به یک مکانیسم قابل اعتماد نیاز داریم: فقط نتیجه یک آزمایش DNA به ما امکان می دهد نتیجه دقیقی بگیریم. equals()این برای روش ما چه معنایی دارد ؟ ما باید آن را در Manکلاس با در نظر گرفتن الزامات برنامه خود لغو کنیم. روش باید int dnaCodeمیدان دو شی را با هم مقایسه کند. اگر مساوی باشند، اجسام برابرند.
@Override
public boolean equals(Object o) {
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
آیا واقعا به همین سادگی است؟ نه واقعا. چیزی را نادیده گرفتیم. برای اشیاء خود، ما فقط یک فیلد را شناسایی کردیم که با ایجاد برابری شیء مرتبط است: dnaCode. حال تصور کنید که ما نه 1، بلکه 50 فیلد مرتبط داریم. و اگر تمام 50 فیلد دو جسم با هم برابر باشند، آنگاه اشیاء برابر هستند. چنین سناریویی نیز ممکن است. مشکل اصلی این است که ایجاد برابری با مقایسه 50 زمینه فرآیندی زمان بر و منابع فشرده است. حال تصور کنید که علاوه بر Manکلاس خود، یک Womanکلاس با همان فیلدهای موجود در Man. اگر برنامه نویس دیگری از کلاس های ما استفاده کند، به راحتی می تواند کدی مانند این بنویسد:
public static void main(String[] args) {

   Man man = new Man(........); // A bunch of parameters in the constructor

   Woman woman = new Woman(.........); // The same bunch of parameters.

   System.out.println(man.equals(woman));
}
در این مورد، بررسی مقادیر فیلد بی معنی است: ما به راحتی می توانیم ببینیم که ما دارای دو کلاس مختلف هستیم، بنابراین هیچ راهی وجود ندارد که آنها با هم برابر باشند! این بدان معنی است که ما باید یک چک به equals()متد اضافه کنیم و کلاس های اشیاء مقایسه شده را با هم مقایسه کنیم. چه خوب که به این فکر کردیم!
@Override
public boolean equals(Object o) {
   if (getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
اما شاید چیز دیگری را فراموش کرده باشیم؟ هوم... حداقل باید بررسی کنیم که یک شی را با خودش مقایسه نمی کنیم! اگر ارجاعات A و B به آدرس حافظه یکسانی اشاره کنند، آنگاه آنها یک شی هستند و نیازی به تلف کردن زمان و مقایسه 50 فیلد نداریم.
@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
همچنین اضافه کردن چک برای: هیچ شیئی نمی nullتواند برابر باشد null. بنابراین، اگر پارامتر متد تهی باشد، دیگر هیچ نکته ای در بررسی های اضافی وجود ندارد. با در نظر گرفتن همه اینها، equals()متد ما برای Manکلاس به شکل زیر است:
@Override
public boolean equals(Object o) {
   if (this == o) return true;
   if (o == null || getClass() != o.getClass()) return false;
   Man man = (Man) o;
   return dnaCode == man.dnaCode;
}
ما تمام بررسی های اولیه ذکر شده در بالا را انجام می دهیم. در پایان روز، اگر:
  • ما دو شی از یک کلاس را با هم مقایسه می کنیم
  • و اشیاء مقایسه شده همان شیء نیستند
  • و شیء عبوری نیستnull
... سپس به مقایسه ویژگی های مربوطه می پردازیم. برای ما، این به معنای dnaCodeفیلدهای دو شی است. هنگام نادیده گرفتن equals()روش، حتما این الزامات را رعایت کنید:
  1. انعکاس پذیری.

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

    if (this == o) return true;

  2. تقارن.

    اگر a.equals(b) == true، پس b.equals(a)باید برگردد true.
    روش ما این نیاز را نیز برآورده می کند.

  3. گذرا.

    اگر دو شیء برابر با شی ثالثی باشند، پس باید با هم برابر باشند.
    اگر a.equals(b) == trueو a.equals(c) == true، سپس b.equals(c)باید true نیز برگردد.

  4. ماندگاری.

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

  5. نابرابری با null.

    برای هر شیء، a.equals(null)باید نادرست را بازگرداند،
    این فقط مجموعه‌ای از «توصیه‌های مفید» نیست، بلکه یک قرارداد سخت‌گیرانه است که در اسناد اوراکل آمده است.

بخش 2: روش هش کد - بهترین روش ها
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION