Equals() и HashCode()

Equals() и HashCode()

EPAM LAB

В этой статье мы рассмотрим два важных метода класса Object в Java. Давай разбираться!

Зачем нужны equals() и hashCode() и в чём их особенность?

При программировании нередко возникают ситуации, когда нужно сравнивать объекты. Первое, что приходит на ум, когда хочется что-то сравнить, это проверить условие написав символ “==”. Кажется, что это должно решить все наши проблемы, однако это не так. Символ “==” имеет смысл использовать только с примитивами. Что касается объектов, “==” в Java сравнивает только ссылки, то есть проверяет, равен ли объект самому себе, а это не всегда может быть полезно. Для сравнения объектов в классе Object присутствует метод equals(), именно он и должен выполнять сравнение объектов. По умолчанию этот метод выполняет внутри себя операцию “==”, однако, идея этого метода состоит в том, что он должен быть переопределён для создаваемых классов.  

Каким же должен быть переопределённый метод equals()?

Метод выглядит следующим образом: 

public boolean equals(Object o); 

На вход он принимает Object и должен вернуть true или false. Теперь пройдемся по шагам.

Как же нужно произвести сравнение двух объектов в Java, чтобы оно выполнялось качественно и эффективно?

1) При сравнении двух объектов, стоит начать с проверки ссылок. Ведь если мы сравниваем объект с самим собой, зачем нам выполнять лишние проверки и сравнивать поля объекта. 

2) Прежде чем приступать к глубокому сравнению, нужно выполнить еще 2 проверки, которые так же могут отсеять объекты, которые точно не равны. Это поможет ускорить работу метода equals(). Это проверки помогают понять, не является ли объект null и сравниваем ли мы объекты одинаковых классов. Если мы изначально сравниваем два null объекта, то первая проверка вернёт нам true, а значит ко второму шагу объект точно не должен быть null.  

3) Теперь, когда мы уверены что оба объекта относятся к одинаковому классу и не равны null, можно выполнить приведение объекта к нужному типу и приступить к сравнению полей.   

Пример

А вот и пример переопределённого метода, который у нас получился: 

Выглядит неплохо, но стоит отметить, что вызываемые методы equals() для String и Integer уже переопределены внутри этих классов. Если полем вашего класса является объект другого вашего класса, в нём так же необходимо переопределять метод equals(). Кроме того, представленный метод полностью соответствует определённым для него требованиям, прописанным в документации Oracle, а именно: 

1. Рефлексивность

Любой объект не равный null должен быть equals() самому себе. 

2. Симметричность

Если a.equals(b) == true, то и b.equals(a) должно возвращать true. 

3. Транзитивность

Если два объекта равны какому-то третьему объекту, значит, они должны быть равны друг и другу. 

4. Постоянность

Результаты работы equals() должны меняться только при изменении входящих в него полей. Если данные двух объектов не менялись, результаты проверки на equals() должны быть всегда одинаковыми.  

5. Неравенство с null

Для любого объекта не равного null проверка a.equals(null) должна возвращать false 

Но что делать если нам нужно лишь убедиться в том, что объекты не равны друг другу и сделать это очень быстро?

Такой подход используется, когда нам нужно хранить набор уникальных объектов, как например в такой структуре данных как HashMap. В этом случае метод equals() не очень подходит, так как проверка всех полей объекта занимает время. В этом случае используется хэш-код объекта

В Java хэш-код объекта можно получить с помощью метода hashCode(). Для любого объекта этот метод должен возвращать 32-битное значение типа int. Возвращаемый хэш-код должен удовлетворять требованиям, прописанным в документации Oracle, а именно: 

  1. Если два объекта равны (т. е. метод equals() возвращает true), у них должен быть одинаковый хэш-код. 
  2. Если метод hashCode() вызывается несколько раз на одном и том же объекте, каждый раз он должен возвращать одно и то же число. 
  3. Одинаковый хэш-код может быть у двух разных объектов. 

Последнее правила вытекает из того, что хэш-код ограничен 32 битами, т. е. может принимать чуть больше 4 миллиардов различных значений, при этом количество создаваемых объектов не ограничено ничем, кроме памяти доступной приложению.  

Пример

Рассмотрим пример того, как может быть переопределён метод hashCode() на примере нашего класса: 

Умножение на простое нечётное число 31 используется для уменьшения числа коллизий, т. е. чтобы разнообразие рассчитываемых хэш-кодов было как можно больше. Есть и более приятный глазу способ переопределения хэш-кода. В классе Objects есть метод hash(), на вход которого нужно подать все поля класса. Этот метод делает то же самое что показано на предыдущей картинке: 

Использование хэш-кода позволяет эффективно работать с элементами в HashMap. О том как именно достигается эта эффективность HashMap, какие проблемы она решает и какие проблемы могут возникнуть при работе с ней, мы расскажем тебе в одной из следующих статей. 


P.S. Если эта статья была для тебя полезной, ставь ❤ и делись ей со своими друзьями! 😊

Источник: Giphy.com

Report Page