CodeGym /בלוג Java /Random-HE /שיטת שווה ב-Java: שיטות עבודה מומלצות
John Squirrels
רָמָה
San Francisco

שיטת שווה ב-Java: שיטות עבודה מומלצות

פורסם בקבוצה
היי! היום נדבר על שתי שיטות חשובות ב-Java: equals() ו- hashCode() . זו לא הפעם הראשונה שאנחנו פוגשים אותם: קורס CodeGym מתחיל בשיעור קצר על equals() - קרא אותו אם שכחת אותו או לא ראית אותו קודם... שיטות equals ו-hashCode: שיטות עבודה מומלצות - 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
נראה שיצרנו שני אובייקטי רכב זהים : הערכים של השדות התואמים של שני אובייקטי המכונית זהים, אך תוצאת ההשוואה עדיין שקרית. אנחנו כבר יודעים את הסיבה: הפניות car1 ו- car2 מצביעות על כתובות זיכרון שונות, כך שהן אינן שוות. אבל אנחנו עדיין רוצים להשוות בין שני האובייקטים, לא שני הפניות. הפתרון הטוב ביותר להשוואת אובייקטים הוא שיטת equals() .

שיטת equals()

אתה אולי זוכר שאנחנו לא יוצרים את השיטה הזו מאפס, אלא אנחנו עוקפים אותה: המתודה equals() מוגדרת במחלקה Object . עם זאת, בצורתו הרגילה, אין בו שימוש מועט:
public boolean equals(Object obj) {
   return (this == obj);
}
כך מוגדרת המתודה equals() במחלקה Object . זו שוב השוואה בין הפניות. למה הם עשו את זה ככה? ובכן, איך יודעים יוצרי השפה אילו אובייקטים בתוכנית שלך נחשבים שווים ואילו לא? :) זו הנקודה העיקרית של שיטת equals() — היוצר של מחלקה הוא זה שקובע באילו מאפיינים נעשה שימוש בעת בדיקת שוויון האובייקטים של המחלקה. לאחר מכן אתה מחליף את שיטת equals() בכיתה שלך. אם אתה לא ממש מבין את המשמעות של "קובע אילו מאפיינים", הבה נשקול דוגמה. הנה מחלקה פשוטה המייצגת גבר: 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 (למען הפשטות, אנו מייצגים זאת כקוד שלם). איזה מהמאפיינים האלה לדעתך יאפשר לתוכנית שלנו לזהות תאומים זהים? שיטות equals ו-hashCode: שיטות עבודה מומלצות - 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. לכן, אם פרמטר השיטה הוא 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()השיטה משמשת כדי להשוות אובייקט כלשהו עם עצמו, היא חייבת להחזיר אמת.
    כבר עמדנו בדרישה הזו. השיטה שלנו כוללת:

    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)חייב גם להחזיר אמת.

  4. הַתמָדָה.

    התוצאה של equals()חייבת להשתנות רק כאשר השדות המעורבים משתנים. אם הנתונים של שני האובייקטים אינם משתנים, התוצאה של equals()חייבת להיות תמיד זהה.

  5. אי שוויון עם null.

    עבור כל אובייקט, a.equals(null)חייב להחזיר false
    זה לא רק קבוצה של כמה "המלצות שימושיות", אלא חוזה קפדני , המפורט בתיעוד של Oracle

חלק 2: שיטת HashCode - שיטות עבודה מומלצות
הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION