JavaRush /Java блог /Random UA /Перевантаження методів equals() та hashCode() в Java
Coder
17 рівень

Перевантаження методів equals() та hashCode() в Java

Стаття з групи Random UA

Перевизначення методів equals() та hashCode() в Java

Equalsі hashCodeє фундаментальними методами, оголошені в класі Objectі містяться в стандартних бібліотеках Java. Перевантаження методів equals() та hashCode() в Java - 1Метод еquals()використовується для порівняння об'єктів, а hashCode- для генерації цілого коду об'єкта. Ці методи широко використовуються в стандартних бібліотеках Java при вставці та вийманні об'єктів у HashMap. Метод equalтакож використовується для забезпечення зберігання тільки унікальних об'єктів HashSetта інших Setреалізаціях, а також у будь-яких інших випадках, коли потрібно порівнювати об'єкти. Реалізація за умовчанням методу equals()в класі java.lang.Objectпорівнює посилання на адресаи в пам'яті, які зберігають змінні, та повертаєtrueТільки в тому випадку, якщо адресаи збігаються, тобто змінні посилаються на той самий об'єкт. Java рекомендує перевизначати методи equals()і hashCode(), якщо передбачається, що порівняння має здійснюватися відповідно до природної логіки або бізнес-логіки. Багато класів у стандартних бібліотеках Java перевизначають їх, наприклад у класі Stringперевизначається equalsтаким чином, що повертається true, якщо вміст двох об'єктів, що порівнюються, однаковий. У класі-обгортці Integerметод equalперевизначається для виконання чисельного порівняння і так далі. Так як HashMapі HashTableв Java покладаються на методи equals()і hashCode()для порівняння своїх keyіvaluesJava пропонує такі правила для перевизначення цих методів:
  1. Рефлексивність: Об'єкт повинен дорівнювати собі самому.
  2. Симетричність: якщо a.equals(b)повертає true, то b.equals(a)повинен також повернути true.
  3. Транзитивність: якщо a.equals(b)повертає trueі b.equals(c)теж повертає true, то c.equals(a)теж має повертати true.
  4. Узгодженість: повторний виклик методу equals()повинен повертати те саме значення до тих пір, поки якесь значення властивостей об'єкта не буде змінено. Тобто, якщо два об'єкти дорівнюють Java, то вони будуть рівні поки їх властивості залишаються незмінними.
  5. Порівняння null: об'єкт повинен бути перевірений на null. Якщо об'єкт дорівнює null, то метод повинен повернути false, а не NullPointerException. Наприклад, a.equals(null)має повернути false.

Угода між equals і hashCode у Java

  1. Якщо об'єкти дорівнюють за результатами виконання методу equals, тоді їх hashcodeмають бути однаковими.
  2. Якщо об'єкти не рівні за результатами виконання методу equals, їх hashcodeможуть бути як однаковими, і різними. Однак для підвищення продуктивності краще, щоб різні об'єкти повертали різні коди.

Як перевизначати метод equals у Java

  1. @Override
    public boolean equals(Object obj) {
    /*1. Перевірте*/if (obj == this) {
    /*і поверніть */ return true;
             }
  2. Перевірте об'єкт на null, а також перевірте, щоб об'єкти були одним типом. Не робіть перевірку за допомогою instanceofтак як така перевірка буде повертати trueдля підкласів і працюватиме правильно тільки у випадку, якщо ваш клас оголошений як immutable. Замість цього можна використовувати getClass();

    if (obj == null || obj.getClass() != this.getClass()) {
                return false;
    }
  3. Оголосіть змінну типу, який ви порівнюєте, та приведіть objдо цього типу. Потім порівнюйте кожен атрибут типу з чисельних атрибутів (якщо є) оскільки чисельні атрибути перевіряються швидше. Порівнюйте атрибути за допомогою операторів І та АБО (так звані short-circuit logical operators) для об'єднання перевірок з іншими атрибутами.

    Person guest = (Person) obj;
            return id == guest.id && (firstName == guest.firstName ||
                (firstName != null && firstName.equals(guest.getFirstName())))
                    && (lastName == guest.lastName || (lastName != null &&                      lastName .equals(guest.getLastName())));
    }
Повний приклад перевизначення методу equals Java
/** * Person class with equals and hashcode implementation in Java * @author Javin Paul */
public class Person {
    private int id;
    private String firstName;
    private String lastName;

    public int getId() { return id; }
    public void setId(int id) { this.id = id;}

    public String getFirstName() { return firstName; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public String getLastName() { return lastName; }
    public void setLastName(String lastName) { this.lastName = lastName; }
    @Override
    public boolean equals(Object obj) {
    if (obj == this) {
        return true;
    }
    if (obj == null || obj.getClass() != this.getClass()) {
        return false;
    }

    Person guest = (Person) obj;
    return id == guest.id
        && (firstName == guest.firstName
            || (firstName != null &&firstName.equals(guest.getFirstName())))        && (lastName == guest.lastName
            || (lastName != null && lastName .equals(guest.getLastName())
            ));
    }
    @Override
    public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());             result = prime * result + id; result = prime * result +
        ((lastName == null) ? 0 : lastName.hashCode()); return result;
    }
 }

Поширені помилки при перевизначенні equals Java

  1. Замість того, щоб перевизначати метод equals (Override)програміст перевантажує його (Overload)Синтаксис методу equals()в класі Objectвизначений як public boolean equals(Object obj), але багато програмістів ненароком перевантажують метод: public boolean equals(Person obj)- замість Objectяк аргумент використовують ім'я свого класу (напр. Person). Цю помилку складно виявити через static binding. Таким чином, якщо ви викличете цей метод для об'єкта свого класу, то метод не просто скомпілюється, а зробить це коректно. Однак, якщо ви покладете ваш об'єкт у колекцію, наприклад, ArrayListі викличете метод contains(), робота якого заснована на методі equals(), то метод containsне зможе виявити ваш об'єкт.

  2. При перевизначенні методу equals()не перевіряти на nullзмінні, що зрештою закінчується NullPointerExceptionпри викликі equals(). Нижче наведено коректний код.

    firstname == guest.firstname || (firstname != null &&
         firstname.equals(guest.firstname));
  3. Третя поширена помилка це не перевизначати метод hashCode(), а лише equals(). Ви повинні перевизначати обидва способи equals()і hashCode()Java. Метод hashCodeвикористовується в hash-колекціях(наприклад HashSet), і чим менше буде колізій (однаковий код при різних об'єктах) тим ефективніше ці колекції працюватимуть з об'єктами вашого класу.

  4. Остання поширена помилка програмістів у цьому, що з перевизначенні методу equals()не зберігається відповідність між методами equals()і compareTo(), що є неформальним вимогою уникнення зберігання дублікатів в Set (SortedSet, TreeSet).

Підказки як писати в Java метод equals

  1. Більшість IDE такі як NetBeans, Eclipse та IntelliJ IDEA забезпечують підтримку генерації методів equals()та hashCode(). У Eclipse натисніть праву кнопку -> source -> generate equals()та hashCode().

  2. Якщо в класі є унікальний бізнес-ключ, то достатньо зробити перевірку тільки на рівність цих полів. Як у нашому прикладі “id” – унікальний номер для кожного Person.

  3. При перевизначенні hashCode()Java перевірте використання всіх полів, які були використані в методі equals().

  4. Stringі класи-оболонки такі як Integer, Floatі Doubleперевизначають метод equals(), але StringBufferне перевизначає.

  5. За будь-якої можливості робіть поля immutableвикористовуючи finalзмінні Java.

  6. При порівнянні Stringоб'єктів використовуйте equals()замість оператора ==.

  7. Два об'єкти, які логічно рівні, але завантажені з різних, ClassLoaderне можуть бути рівними. Пам'ятайте, що перевірка за допомогою getClass()поверне false, якщо клас-завантажувач різний.

  8. Використовуйте @Overrideанотацію також для методу hashCode, оскільки це попереджає невловимі помилки, наприклад значення методу , що повертається int, проте деякі програмісти повертають long.

PS Шановні колеги! Мені виявилася корисною ця стаття при вирішенні завдань 21-го рівня! Бажаю удачі при розборі цієї теми, користуйтесь перекладом! Я сподіваюся, що ви допоможете підняти мій рейтинг, тому що зараз я навіть не можу залишати коментар на цьому форумі. Всім велике спасибі! Оригінал статті Я деякі моменти опустив у зв'язку з нестачею вільного часу, проте переклав усе, що необхідно знати для вирішення завдань 21-го рівня.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ