Автор оригинала: Pankaj Kumar.
Методы Java equals() и hashCode() присутствуют в классе объектов. Таким образом, каждый класс java получает реализацию по умолчанию equals() и hashCode(). В этом посте мы подробно рассмотрим методы java equals() и hashCode ().
Java равно()
Класс объектов, определенный методом equals (), выглядит следующим образом:
public boolean equals(Object obj) {
return (this == obj);
}
Согласно документации java по методу equals (), любая реализация должна соответствовать следующим принципам.
- Для любого объекта x
x.равно(x)должно возвращатьtrue. - Для любых двух объектов x и y
x.равно(y)должно возвращатьtrueтогда и только тогда, когдаy.равно(x)возвращаетtrue. - Для нескольких объектов x, y и z, если
x.равно(y)возвращаетtrueиy.равно(z)возвращаетtrue, тоx.равно(z)должно возвращатьtrue. - Несколько вызовов
x.equals(y)должны возвращать один и тот же результат, если только не изменено какое-либо из свойств объекта, которое используется в реализации методаequals (). - Реализация метода Object class equals() возвращает
trueтолько в том случае, если обе ссылки указывают на один и тот же объект.
Хэш-код Java()
Хэш-код объекта Java() является собственным методом и возвращает целочисленное значение хэш-кода объекта. Общий контракт метода hashCode() заключается в:
- Несколько вызовов hashCode() должны возвращать одно и то же целочисленное значение, если только не изменено свойство объекта, используемое в методе equals ().
- Значение хэш-кода объекта может изменяться при нескольких исполнениях одного и того же приложения.
- Если два объекта равны в соответствии с методом equals (), то их хэш-код должен быть одинаковым.
- Если два объекта неравны в соответствии с методом equals (), их хэш-код не обязательно должен отличаться. Их значение хэш-кода может быть или не быть равным.
Важность метода equals() и хэш-кода()
Метод Java hashCode() и equals() используются в реализациях на основе хэш-таблиц в java для хранения и извлечения данных. Я подробно объяснил это в разделе “Как работает HashMap в java”?
Реализация equals() и hashCode() должна соответствовать этим правилам.
- Если
o1.равно(o2), тоo1.Хэш-код().Хэш-код()всегда должен бытьистинным. - Если
o1.hashCode().hashCodeистинно, это не означает, чтоo1.равно(o2)будетистинно.
Когда переопределять методы equals() и hashCode ()?
Когда мы переопределяем метод equals (), почти необходимо переопределить и метод hashCode (), чтобы наша реализация не нарушила их контракт.
Обратите внимание, что ваша программа не будет выдавать никаких исключений, если контракт equals() и hashCode() нарушен, если вы не планируете использовать класс в качестве ключа хэш-таблицы, то это не создаст никаких проблем.
Если вы планируете использовать класс в качестве ключа хэш-таблицы, то необходимо переопределить методы equals() и hashCode ().
Давайте посмотрим, что происходит, когда мы полагаемся на реализацию методов equals() и hashCode() по умолчанию и используем пользовательский класс в качестве ключа HashMap.
package com.journaldev.java;
public class DataKey {
private String name;
private int id;
// getter and setter methods
@Override
public String toString() {
return "DataKey [name=" + name + ", id=" + id + "]";
}
}
package com.journaldev.java;
import java.util.HashMap;
import java.util.Map;
public class HashingTest {
public static void main(String[] args) {
Map hm = getAllData();
DataKey dk = new DataKey();
dk.setId(1);
dk.setName("Pankaj");
System.out.println(dk.hashCode());
Integer value = hm.get(dk);
System.out.println(value);
}
private static Map getAllData() {
Map hm = new HashMap<>();
DataKey dk = new DataKey();
dk.setId(1);
dk.setName("Pankaj");
System.out.println(dk.hashCode());
hm.put(dk, 10);
return hm;
}
}
Когда мы запустим вышеуказанную программу, она выведет null . Это связано с тем, что метод хэш-кода объекта() используется для поиска корзины для поиска ключа. Поскольку у нас нет доступа к ключам HashMap, и мы снова создаем ключ для извлечения данных, вы заметите, что значения хэш-кода обоих объектов различны, и, следовательно, значение не найдено.
Реализация метода equals() и хэш-кода()
Мы можем определить нашу собственную реализацию методов equals() и hashCode (), но если мы не реализуем их тщательно, во время выполнения могут возникнуть странные проблемы. К счастью, в настоящее время большинство IDE предоставляют способы их автоматической реализации, и при необходимости мы можем изменить их в соответствии с нашими требованиями.
Мы можем использовать Eclipse для автоматической генерации методов equals() и hashCode ().
Вот автоматически сгенерированные реализации методов equals() и hashCode ().
@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;
DataKey other = (DataKey) 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() используют одни и те же поля для вычислений, поэтому их контракт остается действительным.
Если вы снова запустите тестовую программу, мы получим объект с карты, и программа напечатает 10.
Мы также можем использовать Project Lombok для автоматической генерации эквивалентов и реализации метода хэш-кода.
Что такое столкновение хэшей
Говоря очень простыми словами, реализации хэш-таблиц Java используют следующую логику для операций get и put.
- Сначала определите “корзину” для использования, используя хэш-код “ключа”.
- Если в корзине нет объектов с одинаковым хэш-кодом, добавьте объект для операции put и верните значение null для операции get.
- Если в корзине есть другие объекты с тем же хэш-кодом, то в игру вступает метод “ключ” равен.
- Если функция equals() возвращает true и это операция put, то значение объекта переопределяется.
- Если функция equals() возвращает false и это операция put, то в корзину добавляется новая запись.
- Если функция equals() возвращает true и это операция get, то возвращается значение объекта.
- Если функция equals() возвращает false и это операция get, то возвращается значение null.
На рисунке ниже показаны элементы корзины HashMap и то, как связаны их значения() и хэш-код ().
Явление, когда два ключа имеют одинаковый хэш-код, называется столкновением хэшей. Если метод hashCode() не реализован должным образом, произойдет большее количество столкновений хэшей, и записи карты не будут правильно распределены, что приведет к замедлению операций получения и размещения. Это является причиной использования простых чисел при генерации хэш-кода, чтобы записи карты правильно распределялись по всем сегментам.
Что делать, если мы не реализуем как hashCode (), так и equals()?
Мы уже видели выше, что если hashCode() не реализован, мы не сможем получить значение, потому что HashMap использует хэш-код для поиска корзины для поиска записи.
Если мы используем только хэш-код() и не реализуем функцию equals (), то также значение не будет получено, потому что метод equals() вернет значение false.
Рекомендации по реализации методов equals() и hashCode()
- Используйте одни и те же свойства в реализациях методов equals() и hashCode (), чтобы их контракт не нарушался при обновлении каких-либо свойств.
- Лучше использовать неизменяемые объекты в качестве ключа хэш-таблицы, чтобы мы могли кэшировать хэш-код, а не вычислять его при каждом вызове. Вот почему строка является хорошим кандидатом на ключ хэш-таблицы, потому что она неизменна и кэширует значение хэш-кода.
- Реализуйте метод hashCode() таким образом, чтобы происходило наименьшее количество столкновений хэшей и записи равномерно распределялись по всем сегментам.