Вступление:
Класс Java Object обеспечивает базовую реализацию методов — hashCode () и equals (). Эти методы чрезвычайно полезны, особенно при работе со структурой Collection. Реализации хеш-таблицы полагаются на эти методы для хранения и извлечения данных.
В этом уроке мы узнаем о контракте между hashCode () и equals () , их реализациями по умолчанию. Мы также поговорим о том, когда и как переопределить эти методы.
Поведение по умолчанию:
Давайте сначала посмотрим на реализации этих методов по умолчанию:
1. равно ():
Метод equals (), представленный в классе Object, просто сравнивает ссылки на объекты:
| 1 2 3 | publicbooleanequals(Object obj) {    return(this== obj);} | 
Таким образом, по умолчанию obj1.equals (obj2) совпадает с obj1 == obj2 .
Метод equals () сравнивает фактические значения для таких классов, как String и т. Д., Поскольку он переопределяется в этих соответствующих классах.
2. hashCode ():
Подпись метода hashCode () в JDK:
| 1 | publicnativeinthashCode(); | 
Здесь ключевое слово 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 | publicclassEmployee {     privateintid;    privateString name;     //constructors, getters, setters, toString implementations } | 
И HashMap, хранящий Employee в качестве ключей:
| 1 2 3 4 | Map<Employee, Integer> map = newHashMap<>(); map.put(newEmployee(1, "Sam"), 1);map.put(newEmployee(2, "Sierra"), 2); | 
Теперь, когда мы вставили две записи, давайте попробуем проверить containsKey () :
| 1 | booleancontainsSam = map.containsKey(newEmployee(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 | publicclassEmployee {     ...         @Override    publicinthashCode() {        finalintprime = 31;        intresult = 1;        result = prime * result + id;        result = prime * result + ((name == null) ? 0: name.hashCode());        returnresult;    }     @Override    publicbooleanequals(Object obj) {        if(this== obj)            returntrue;        if(obj == null)            returnfalse;        if(getClass() != obj.getClass())            returnfalse;        Employee other = (Employee) obj;        if(id != other.id)            returnfalse;        if(name == null) {            if(other.name != null)                returnfalse;        }         elseif(!name.equals(other.name))            returnfalse;         returntrue;    }} | 
Ясно, что одни и те же поля использовались для реализации методов equals () и hashCode (), чтобы не отставать от контракта.
Лучшие практики:
Вот некоторые из лучших практик, которые следует соблюдать при работе с equals () и hashCode () :
- Реализуйте hashCode () для равномерного распределения элементов по различным сегментам. Идея состоит в том, чтобы минимизировать количество столкновений и, в свою очередь, иметь хорошую производительность
- Мы должны использовать одинаковые поля для реализаций equals () и hashCode ().
- Предпочитайте неизменяемые объекты как ключи в HashMap, поскольку они поддерживают кэширование значений хеш-кода
- При работе с инструментом ORM всегда используйте геттеры вместо полей в определении методов hashCode () и equals () . Это потому, что несколько полей могут быть загружены лениво
Вывод:
В этом уроке мы сначала посмотрели на стандартные реализации методов equals () и hashCode () . Позже мы обсудили, когда и как переопределить эти методы.
Оставьте первый комментарий.
| Опубликовано на Java Code Geeks с разрешения Шубхры Шриваставы, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Java equals () и hashCode () Мнения, высказанные участниками Java Code Geeks, являются их собственными. |