JavaRush /Java блог /Random UA /Робота з методами hashCode() та equals() у мові Java
Lenchik854
0 рівень
Chernihiv

Робота з методами hashCode() та equals() у мові Java

Стаття з групи Random UA
У цьому пості викладу своє розуміння методів hashCode()і equals(). Я хочу розповісти про їхню реалізацію за умовчанням, а також про те, як коректно перевизначити їх. Я також напишу про реалізацію цих методів, використовуючи Apache Common package's допоміжні класи. Робота з методами hashCode() та equals() у мові Java - 1Зміст цієї посади:
  1. Використання hashCode()та equals().
  2. Перевизначення стандартної поведінки.
  3. Перевизначення hashCode()та equals(), використовуючи Apache Commons Lang.
  4. Те, що важливо пам'ятати.
  5. Особлива увага При використанні ORM.
Методи hashCode()були equals()визначені в класі Object, який є батьківським класом для об'єктів java. Тому всі java об'єкти успадковують від цих методів реалізацію за умовчанням.

Використання hashCode() та equals()

Метод hashCode()використовується для отримання цілого цілого номера для даного об'єкта. Коли необхідно зберегти об'єкт як структуру даних у якійсь хеш-таблиці (такий об'єкт також називають кошиком - bucket), цей номер використовується для визначення його місцезнаходження у цій таблиці. За промовчанням метод hashCode()об'єкта повертає номер комірки пам'яті, де об'єкт зберігається. Метод equals(), як і з його назви, використовується для простої перевірки рівності двох об'єктів. Реалізація цього методу за замовчуванням просто перевіряє за посиланнями два об'єкти щодо їх еквівалентності.

Перевизначення поведінки за промовчанням

Все працює добре, поки ви не перевизначаєте жоден з цих методів у своїх класах. Але іноді в програмах необхідно змінювати стандартну поведінку деяких об'єктів. Давайте візьмемо приклад, де у вашому додатку є об'єкт Employee. Давайте напишемо мінімально можливу структуру такого класу.
public class Employee
{
    private Integer id;
    private String firstname;
    private String lastName;
    private String department;

    public Integer getId() {
        return id;
    }
    public void setId(Integer 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;
    }
    public String getDepartment() {
        return department;
    }
    public void setDepartment(String department) {
        this.department = department;
    }
}
Описаний вище клас Employeeмає деякі основні атрибути та методи доступу. Зараз розглянемо просту ситуацію, де необхідно порівняти два об'єкти класу Employee.
public class EqualsTest {
    public static void main(String[] args) {
        Employee e1 = new Employee();
        Employee e2 = new Employee();

        e1.setId(100);
        e2.setId(100);
        //Печатает false в консоли
        System.out.println(e1.equals(e2));
    }
}
Не треба бути ясновидячим, щоб здогадатися, що написаний вище метод поверне “false”. Але чи правильно це насправді, враховуючи, що ці обидва об'єкти однакові? У реальному часі application метод повинен повернути true. Щоб досягти коректної поведінки, нам потрібно перевизначити метод equals(), як і зроблено нижче:
public boolean equals(Object o) {
        if(o == null)
        {
            return false;
        }
        if (o == this)
        {
           return true;
        }
        if (getClass() != o.getClass())
        {
            return false;
        }
        Employee e = (Employee) o;
        return (this.getId() == e.getId());
}
Додайте цей метод до свого класу Employee, і перевірка на еквівалентність поверне “true”. Однак, чи ми все зробабо? Поки немає. Перевіримо наш модифікований клас ще одним способом.
import java.util.HashSet;
import java.util.Set;

public class EqualsTest
{
    public static void main(String[] args)
    {
        Employee e1 = new Employee();
        Employee e2 = new Employee();

        e1.setId(100);
        e2.setId(100);

        //Печатает 'true'
        System.out.println(e1.equals(e2));

        Set employees = new HashSet();
        employees.add(e1);
        employees.add(e2);
        //Печатает два об'єкта
        System.out.println(employees);
    }
}
Команда System.out.println(employee)друкує два об'єкти. Якщо обидва об'єкти були еквівалентні, а Setмістяться тільки унікальні об'єкти, то всередині HashSetповинен бути тільки один екземпляр, тобто. обидва об'єкти посилаються на однакові екземпляри класу Employee. Що ж ми пропустабо? Ми пропустабо другий важливий метод hashCode(). Як сказано в документації java, якщо ви перевизначаєте спосіб equals(), то ви повинні перевизначити спосіб hashCode(). Отже, давайте додамо ще один метод у наш клас Employee.
@Override
 public int hashCode()
 {
    final int PRIME = 31;
    int result = 1;
    result = PRIME * result + getId();
    return result;
 }
Ми додали один раз цей метод до нашого класу, і на друк буде виведено лише один об'єкт, і, таким чином, перевірка еквівалентності е1 та е2 показала true.

Перевизначення hashCode()та equals(), використовуючи Apache Commons Lang

Apache Commons надає два чудові допоміжні класи для виклику методів hashCode()і equals(). Нижче дивимося використання:
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Employee
{
 private Integer id;
 private String firstname;
 private String lastName;
 private String department;
public Integer getId() {
    return id;
 }
 public void setId(Integer 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;
 }
 public String getDepartment() {
    return department;
 }
 public void setDepartment(String department) {
    this.department = department;
 }
@Override
 public int hashCode()
 {
    final int PRIME = 31;
    return new HashCodeBuilder(getId()%2==0?getId()+1:getId(), PRIME).
           toHashCode();
 }
@Override
 public boolean equals(Object o) {
    if (o == null)
       return false;
    if (o == this)
       return true;
    if (o.getClass() != getClass())
       return false;
    Employee e = (Employee) o;
       return new EqualsBuilder().
              append(getId(), e.getId()).
              isEquals();
    }
 }
З іншого боку, якщо ви використовуєте один з редакторів коду, вони також повинні бути здатні викликати деякі хороші структури для вас. Наприклад, якщо в Eclipse IDE натиснути правою клавішею на class >> sourse > Generating hashCode() and equals() … буде згенерована дуже хороша реалізація для вас. Робота з методами hashCode() та equals() у мові Java - 2Те, що важливо пам'ятати.
  1. Завжди використовуйте самі атрибути об'єкта для виклику та hashCode()та equals(). Саме в нашому випадку ми використовували employee id.
  2. Метод equals()має бути стійким (якщо об'єкт не змінювався, метод повинен повертати те саме значення).
  3. Щоразу, коли a.equals(b), то a.hashCode()має бути таким же, як b.hashCode().
  4. Якщо ви перевизначабо один метод, то обов'язково маємо перевизначити другий.

Особлива Увага При використанні ORM

Якщо ви маєте справу з ORM (ru.wikipedia.org/wiki/ORM), то завжди використовуйте гетери і ніколи не використовуйте в hashCode()і equals()посилання на поля. Це тому, що в ORM, іноді поля завантажуються за допомогою відкладеного завантаження (lazy load) і не доступні, поки не викликані їх гетери. Наприклад, у нашому класі Employeeми використовуємо e1.id == e2.id. Цілком можливо, що поля id завантажені за допомогою відкладеного завантаження. Одне з полів може бути 0 або null і ми отримаємо некоректну поведінку. Але якщо використовується e1.getId() == e2.getId(), ми можемо бути впевнені, навіть якщо поля були завантажені за допомогою відкладеного завантаження; виклик геттера першим заповнить поле. Це все, що я знаю про методи hashCode()іequals(). Сподіваюся, що це десь комусь допоможе. Успіхів в навчанні!! ps Це моя перша спроба перекладу. Постаралася передати якомога ближче до того, що хотів сказати автор. Якщо є зауваження, будь ласка, напишіть у коментарях. Строго не судіть :-))) Oригінал статті
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ