Посмотрите на следующие пункты из https://docs.oracle.com/javase/9/docs/api/java/lang/Object.html#hashCode -
- Всякий раз, когда он вызывается для одного и того же объекта более одного раза во время выполнения приложения Java, метод hashCode должен последовательно возвращать одно и то же целое число при условии, что никакая информация, используемая в сравнениях сравнений объекта, не изменяется. Это целое число не должно оставаться согласованным при выполнении одного приложения другим исполнением того же приложения.
- Если два объекта равны в соответствии с методом equals (Object), то вызов метода hashCode для каждого из двух объекты должны давать один и тот же целочисленный результат.
- Не требуется, чтобы, если два объекта были неравны в соответствии с методом equals (java .lang.Object), то вызывать метод hashCode для каждого из двух объектов должен давать разные целочисленные результаты. Однако программист должен знать, что выдача различных целочисленных результатов для неравных объектов может повысить производительность таблиц ha sh.
- Насколько это практически целесообразно, метод hashCode, определенный классом Object, возвращает различные целые числа для отдельных объектов. (Хэш-код может быть или не быть реализован как некоторая функция адреса памяти объекта в определенный момент времени.)
Теперь рассмотрим следующий код и его вывод:
class MyEmployee {
String code;
String name;
int age;
public MyEmployee(String code, String name, int age) {
super();
this.code = code;
this.name = name;
this.age = age;
}
}
public class Main {
public static void main(String[] args) {
MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee3 = employee1;
System.out.println(employee1.equals(employee3));
System.out.println("employee1.hashCode(): " + employee1.hashCode());
System.out.println("employee3.hashCode(): " + employee3.hashCode());
System.out.println(employee1.equals(employee2));
System.out.println("employee2.hashCode(): " + employee2.hashCode());
}
}
Вывод:
true
employee1.hashCode(): 511833308
employee3.hashCode(): 511833308
false
employee2.hashCode(): 1297685781
Поскольку employee3
указывает на тот же объект, что и employee1
, вы получаете тот же хеш-код для них, а employee2
указывает на другой объект (хотя он имеет такое же содержимое, ключевое слово, new
создаст отдельный объект в памяти), и поэтому вы можете редко получить тот же хеш-код для employee2
, что и point # 4 упомянутое выше из документации гласит: As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects.
Вы должны переопределить метод hashCode
так, чтобы он возвращал один и тот же хэш-код для двух объектов с одинаковым содержимым, например
class MyEmployee {
String code;
String name;
int age;
public MyEmployee(String code, String name, int age) {
super();
this.code = code;
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((code == null) ? 0 : code.hashCode());
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
}
public class Main {
public static void main(String[] args) {
MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee3 = employee1;
System.out.println(employee1.equals(employee3));
System.out.println("employee1.hashCode(): " + employee1.hashCode());
System.out.println("employee3.hashCode(): " + employee3.hashCode());
System.out.println(employee1.equals(employee2));
System.out.println("employee2.hashCode(): " + employee2.hashCode());
}
}
Вывод:
true
employee1.hashCode(): 128107556
employee3.hashCode(): 128107556
false
employee2.hashCode(): 128107556
Реализация hashCode
, приведенная выше, дает один и тот же хэш-код для employee1
и employee2
, даже если equals
возвращает false
(отметьте как * 1048) * пункт № 3 , упомянутый выше из документа mentation).
Неправильный способ переопределения hashCode
может привести к тому, что даже одни и те же объекты будут возвращать разные хеш-коды, например,
class MyEmployee {
String code;
String name;
int age;
public MyEmployee(String code, String name, int age) {
super();
this.code = code;
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((code == null) ? 0 : (int) (code.length() * (Math.random() * 100)));
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
}
public class Main {
public static void main(String[] args) {
MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee3 = employee1;
System.out.println(employee1.equals(employee3));
System.out.println("employee1.hashCode(): " + employee1.hashCode());
System.out.println("employee1.hashCode() again: " + employee1.hashCode());
System.out.println("employee3.hashCode(): " + employee3.hashCode());
System.out.println(employee1.equals(employee2));
System.out.println("employee2.hashCode(): " + employee2.hashCode());
}
}
Вывод:
true
employee1.hashCode(): 66066760
employee1.hashCode() again: 66069457
employee3.hashCode(): 66073797
false
employee2.hashCode(): 66074882
Это неправильный способ переопределения hashCode
, потому что вызов hashCode
для одного и того же объекта более одного раза во время выполнения Java приложения должен последовательно возвращать одно и то же целое число (отметьте как point # 1 , упомянутых выше в документации).
Теперь рассмотрим следующий код и его вывод:
class MyEmployee {
String code;
String name;
int age;
public MyEmployee(String code, String name, int age) {
super();
this.code = code;
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
MyEmployee other = (MyEmployee) obj;
if (code == null) {
if (other.code != null)
return false;
} else if (!code.equals(other.code))
return false;
return true;
}
}
public class Main {
public static void main(String[] args) {
MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee3 = employee1;
System.out.println(employee1.equals(employee3));
System.out.println("employee1.hashCode(): " + employee1.hashCode());
System.out.println("employee3.hashCode(): " + employee3.hashCode());
System.out.println(employee1.equals(employee2));
System.out.println("employee2.hashCode(): " + employee2.hashCode());
}
}
Вывод:
true
employee1.hashCode(): 511833308
employee3.hashCode(): 511833308
true
employee2.hashCode(): 1297685781
Поскольку employee1.equals(employee2)
возвращает true
, хэш-код также должен быть возвращен таким же (отметьте пункт # 2 , упомянутый выше в документации). Однако значения хеш-кода employee1
и employee2
отличаются, что не является правильным. Это различие заключается в том, что мы не переопределили метод hashCode
. Таким образом, всякий раз, когда вы переопределяете equals
, вы также должны корректно переопределять hashCode
.
Наконец, ниже приведен правильный способ реализации hashCode
и equals
:
class MyEmployee {
String code;
String name;
int age;
public MyEmployee(String code, String name, int age) {
super();
this.code = code;
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((code == null) ? 0 : code.hashCode());
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;
MyEmployee other = (MyEmployee) obj;
if (age != other.age)
return false;
if (code == null) {
if (other.code != null)
return false;
} else if (!code.equals(other.code))
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
public class Main {
public static void main(String[] args) {
MyEmployee employee1 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee2 = new MyEmployee("AB12", "Dhruv", 24);
MyEmployee employee3 = employee1;
System.out.println(employee1.equals(employee3));
System.out.println("employee1.hashCode(): " + employee1.hashCode());
System.out.println("employee3.hashCode(): " + employee3.hashCode());
System.out.println(employee1.equals(employee2));
System.out.println("employee2.hashCode(): " + employee2.hashCode());
}
}
Вывод:
true
employee1.hashCode(): 128107556
employee3.hashCode(): 128107556
true
employee2.hashCode(): 128107556