Вступление:
Класс Java Object обеспечивает базовую реализацию методов — hashCode () и equals (). Эти методы чрезвычайно полезны, особенно при работе со структурой Collection. Реализации хеш-таблицы полагаются на эти методы для хранения и извлечения данных.
В этом уроке мы узнаем о контракте между hashCode () и equals () , их реализациями по умолчанию. Мы также поговорим о том, когда и как переопределить эти методы.
Поведение по умолчанию:
Давайте сначала посмотрим на реализации этих методов по умолчанию:
1. равно ():
Метод equals (), представленный в классе Object, просто сравнивает ссылки на объекты:
1
2
3
|
public boolean equals(Object obj) { return ( this == obj); } |
Таким образом, по умолчанию obj1.equals (obj2) совпадает с obj1 == obj2 .
Метод equals () сравнивает фактические значения для таких классов, как String и т. Д., Поскольку он переопределяется в этих соответствующих классах.
2. hashCode ():
Подпись метода hashCode () в JDK:
1
|
public native int hashCode(); |
Здесь ключевое слово native указывает, что метод реализован в собственном коде с использованием JNI (Java Native Interface).
Метод hashCode () возвращает тип int. Возвращаемое значение по умолчанию представляет адрес памяти объекта.
Принципы реализации:
Прежде чем мы переопределим методы equals () и hashCode () , давайте сначала посмотрим на рекомендации:
1. равно (): Наша реализация метода equals () должна быть:
- Рефлексивный: для любого ссылочного значения obj , obj.equals (obj) должно возвращать true
- Симметричный: для ссылочных значений obj1 и obj2 , если obj1.equals (obj2) равен true, тогда obj2.equals (obj2) также должен возвращать true
- Транзитивно: для ссылочных значений obj1, obj2 и obj3 , если obj1.equals (obj2) имеет значение true и obj2.equals (obj3) имеет значение true, тогда obj1.equals (obj3) также должен возвращать true
- Согласованно: при условии, что мы не изменили реализацию, множественные вызовы метода equals () всегда должны возвращать одно и то же значение
2. hashCode (): при реализации hashCode () мы должны учитывать следующие моменты:
- В одном выполнении несколько вызовов hashCode () должны возвращать одно и то же значение, при условии, что мы не меняем свойство в реализации equals ()
- равные объекты должны возвращать одинаковое значение hashCode ()
- два или более неравных объекта могут иметь одинаковое значение hashCode ()
Контракт equals () и hashCode () :
Хотя все вышеперечисленные принципы необходимо учитывать при переопределении этих методов, среди них есть одно популярное правило:
Для двух объектов obj1 и obj2
- Если obj1.equals (obj2), то obj1.hashCode () = obj2.hashCode () должно быть истинным
- Однако, если obj1.hashCode () == obj2.hashCode () , то obj1.equals (obj2) может возвращать либо true, либо false, т. Е. Obj1 и obj2 могут или не могут быть равны
Обычно это называется контрактом equals () и hashCode () .
Почему переопределить equals () и hashCode ()?
Методы hashCode () и equals () играют важную роль в хранении и извлечении элементов в реализации на основе хеш-таблицы. HashCode () определяет корзину, на которую отображается данный элемент. Внутри блока метод equals () используется для поиска данной записи.
Допустим, у нас есть класс Employee :
1
2
3
4
5
6
7
8
|
public class Employee { private int id; private String name; //constructors, getters, setters, toString implementations } |
И HashMap, хранящий Employee в качестве ключей:
1
2
3
4
|
Map<Employee, Integer> map = new HashMap<>(); map.put( new Employee( 1 , "Sam" ), 1 ); map.put( new Employee( 2 , "Sierra" ), 2 ); |
Теперь, когда мы вставили две записи, давайте попробуем проверить containsKey () :
1
|
boolean containsSam = map.containsKey( new Employee( 1 , "Sam" )); //false |
Несмотря на то, что у нас есть запись для Сэма , containsKey () вернул false . Это потому, что мы еще не переопределили методы equals () и hashCode () . И по умолчанию equals () просто делает сравнение на основе ссылок.
Переопределение equals () и hashCode () :
Согласно Javadocs:
Когда мы переопределяем метод equals () , мы также должны переопределить метод hashCode ()
Это поможет избежать нарушения контракта equals-hashCode .
Обратите внимание, что компилятор не будет жаловаться, если мы нарушим контракт, но мы можем столкнуться с неожиданным поведением, скажем, когда мы храним такие объекты как ключи в HashMap .
Мы можем быстро переопределить эти методы, используя функцию IDE. При использовании Eclipse мы можем перейти к Source-> Generate hashCode () и equals (). Давайте посмотрим на сгенерированные реализации для нашего класса Employee :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
public class Employee { ... @Override public int hashCode() { final int prime = 31 ; int result = 1 ; result = prime * result + id; result = prime * result + ((name == null ) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if ( this == obj) return true ; if (obj == null ) return false ; if (getClass() != obj.getClass()) return false ; Employee other = (Employee) obj; if (id != other.id) return false ; if (name == null ) { if (other.name != null ) return false ; } else if (!name.equals(other.name)) return false ; return true ; } } |
Ясно, что одни и те же поля использовались для реализации методов equals () и hashCode (), чтобы не отставать от контракта.
Лучшие практики:
Вот некоторые из лучших практик, которые следует соблюдать при работе с equals () и hashCode () :
- Реализуйте hashCode () для равномерного распределения элементов по различным сегментам. Идея состоит в том, чтобы минимизировать количество столкновений и, в свою очередь, иметь хорошую производительность
- Мы должны использовать одинаковые поля для реализаций equals () и hashCode ().
- Предпочитайте неизменяемые объекты как ключи в HashMap, поскольку они поддерживают кэширование значений хеш-кода
- При работе с инструментом ORM всегда используйте геттеры вместо полей в определении методов hashCode () и equals () . Это потому, что несколько полей могут быть загружены лениво
Вывод:
В этом уроке мы сначала посмотрели на стандартные реализации методов equals () и hashCode () . Позже мы обсудили, когда и как переопределить эти методы.
Оставьте первый комментарий.
Опубликовано на Java Code Geeks с разрешения Шубхры Шриваставы, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Java equals () и hashCode () Мнения, высказанные участниками Java Code Geeks, являются их собственными. |