为什么具有相同值的两个对象的hashcode()在java中是不同的?



Object-1:

Employee employee1 = new Employee("AB12","Dhruv", 24);

对象 2:

Employee employee2 = new Employee("AB12", "Dhruv", 24);

输出

HASH CODE FOR EMPLOYEE 1 :  43704527 
HASH CODE FOR EMPLOYEE 2 : 158893348

从https://docs.oracle.com/javase/9/docs/api/java/lang/Object.html#hashCode 来看以下几点--

  1. 每当在 Java 应用程序执行期间多次对同一对象调用 hashCode 方法时,只要不修改对象上相等比较中使用的任何信息,hashCode 方法必须始终返回相同的整数。从应用程序的一次执行到同一应用程序的另一次执行,此整数不必保持一致。
  2. 如果根据 equals(Object( 方法将两个对象相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。
  3. 如果根据 equals(java.lang.Object( 方法,两个对象不相等,则对两个对象中的每一个调用 hashCode 方法必须产生不同的整数结果,则不需要。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。
  4. 在合理可行的范围内,类 Object 定义的 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;
}
}
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将在内存中创建单独的对象(,因此,您可能很少获得与上面提到的point#4相同的employee2哈希代码从文档中指出: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的实现为employee1employee2生成相同的哈希代码,即使equals返回false(检查上面文档中提到的点#3(。

覆盖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的错误方法,因为在执行 Java 应用程序期间多次调用同一对象上的hashCode必须始终返回相同的整数(检查上面文档中提到的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(。但是,employee1employee2的哈希码值不同,这是不正确的。这种差异是因为我们没有重写hashCode方法。因此,无论何时覆盖equals,您也应该以正确的方式覆盖hashCode

最后,给出以下实现hashCodeequals的正确方法:

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

除非您@OverridehashCode()方法并实现自己的版本,否则您的类将使用从Object继承的默认方法,该方法为每个对象实例生成唯一的哈希代码。

如果您希望equals和/或hashCode只关心对象的成员,则需要自己编写实现。

因为您尚未覆盖hashCode来替换默认实现。覆盖equals不会更改hashCode(如果您覆盖equals,则几乎总是必须覆盖两者(。

hashCode文档对Object中的默认实现进行了说明:

在合理可行的范围内,哈希代码方法由 类对象确实为不同对象返回不同的整数。(这 通常通过转换 对象成整数,但这种实现技术不是 Java™ 编程语言需要。

因此,对于不同的实例,即使它们是等效的,您也会得到不同的整数(通常(,除非您替换hashCode

你可以这样做

@Override
public int hashCode() {
return hashId;
}

最新更新