两个对象的 hashCode()相同,equals()也为 true,两个对象一定相同吗?

2022-10-11 09:27:45

hashCode(),equals() 示例讲解

两个对象的 hashCode()相同,equals()也为 true,两个对象一定相同吗?

我们写一个类来了解一下hashCode() 和 equals()

publicclassPerson{private String name;privatePerson(String name){this.name= name;}@OverridepublicinthashCode(){return1;}@Overridepublicbooleanequals(Object obj){if(objinstanceofPerson){
   Person p=(Person) obj;returnthis.name.equals(p.name);}returnfalse;}

这里自定义了一个Person类并且重写了hashCode()和equals()方法。其中hashCode()方法始终返回 1(只是测试使用,实际不应该如此设计),而equals()方法则通过比较两个Person对象的属性 name(String类型)的值 返回布尔值。
接下来 我们来写一段测试代码。

publicstaticvoidmain(String[] args){
  String str="a";
  Person a=newPerson(str);
  Person b=newPerson(str);
  
  
  System.out.println(a);
  System.out.println(b);
  System.out.println("a.hashCode: "+a.hashCode()+"  b.hashCode: "+b.hashCode());
  System.out.println("equals: "+a.equals(b));
  System.out.println(a==b);}

运行结果:
在这里插入图片描述
这里我们看出即使重写了hashCode()和equals()方法,使得对象a和b的hash值相同,equals方法也为true。a==b 依然为false。由此可见"==“运算符实际是比较的是地址引用,引用对象指向的地址如果相同,则返回true 。并不依赖我们重写了的hashCode()和equals()方法。所以简单的考虑,两个new出来的对象,一定是存在不同的内存地址中。所以判断a==b一定是false。那么也就是说 hash值相同,equals比较也为true的两个对象不一定就是相等的,这里的相等指的是”=="运算符,而不是equals()方法。
那么 重写hashcode() 和equals()有何作用呢;这里我们引用一个容器 HashSet。Set容器要求存入Set的每个元素都必须是唯一的,Set不保存重复元素。而存入HashSet的元素必须定义hashCode()方法 (出自thinking in java

我们添加几行代码

publicstaticvoidmain(String[] args){
  String str="a";
  Person a=newPerson(str);
  Person b=newPerson(str);
  HashSet<Person> set=newHashSet<>();
  set.add(a);
  set.add(b);
  System.out.println(a);
  System.out.println(b);
  System.out.println("a.hashCode: "+a.hashCode()+"  b.hashCode: "+b.hashCode());
  System.out.println("equals: "+a.equals(b));
  System.out.println(a==b);
  System.out.println(set+"  set.size= "+set.size());}

运行结果:
在这里插入图片描述
运行结果显示 HashSet里只存放了一个对象,即Set容器认为Person a 和Person b为同一个对象,那么它是怎么判断的呢?
我们再次修改代码,这次注释掉重写的hashCode()方法

publicclassPerson{private String name;publicPerson(String name){this.name= name;}// @Override// public int hashCode() {////  return 1;// }@Overridepublicbooleanequals(Object obj){if(objinstanceofPerson){
   Person p=(Person) obj;returnthis.name.equals(p.name);}returnfalse;}publicstaticvoidmain(String[] args){
  String str="a";
  Person a=newPerson(str);
  Person b=newPerson(str);
  HashSet<Person> set=newHashSet<>();
  set.add(a);
  set.add(b);
  System.out.println(a);
  System.out.println(b);
  System.out.println("a.hashCode: "+a.hashCode()+"  b.hashCode: "+b.hashCode());
  System.out.println("equals: "+a.equals(b));
  System.out.println(a==b);
  System.out.println(set+"  set.size= "+set.size());}}

运行结果:
在这里插入图片描述
现在Set中有两个Person对象,也就是说Set认为Person a 和 Person b是两个不同的对象了。因为a和b的HashCode值不同。可是我们已经重写了equals()方法,我们希望通过属性name来确定对象是否相同,即name相同的对象就认为是同一个对象。这显然是不符合我们的预期的,这个问题我们稍后会继续讨论。接下来考虑这种情况:如果equals()方法返回false,但hashcode相同的对象,HashSet会如何判断呢?
我们接着修改代码,这次给a和b的name属性赋不同的值

publicmain(String[] args){
  Person a=newPerson("a");
  Person b=newPerson("b");
  HashSet<Person> set=newHashSet<>();
  set.add(a);
  set.add(b);
  System.out.println(a);
  System.out.println(b);
  System.out.println("a.hashCode: "+a.hashCode()+"  b.hashCode: "+b.hashCode());
  System.out.println("equals: "+a.equals(b));
  System.out.println(a==b);
  System.out.println(set+"  set.size= "+set.size());}

运行结果:
在这里插入图片描述

现在我们看到Set中存了两个Person对象,它们的HashCode相同,但是name值不同。HashSet认为是不同的两个对象。那么也就是说HashSet在判断两个对象是否相同时,首先会判断它们的hashCode是否相同。hashCode不同则为不同的对象。如果hashCode相同则接着调用equals()方法,equals方法为true则为同一个对象,false则为不同的对象。
回到刚才的的问题,如果我们需要通过equals()方法来判断对象是否相等,则应该设计合适的hashCode()方法,即遵循equals()为true的两个对象返回相同的hashCode。为保持这个特性。我们需要简单修改一下Person类的hashCode()方法,返回属性name的hashCode即可。

@OverridepublicinthashCode(){return name.hashCode();//返回属性name的hashCode}@Overridepublicbooleanequals(Object obj){if(objinstanceofPerson){
   Person p=(Person) obj;returnthis.name.equals(p.name);}returnfalse;}publicstaticvoidmain(String[] args){
  Person a=newPerson("a");
  Person b=newPerson("a");
  HashSet<Person> set=newHashSet<>();
  set.add(a);
  set.add(b);
  System.out.println(a);
  System.out.println(b);
  System.out.println("a.hashCode: "+a.hashCode()+"  b.hashCode: "+b.hashCode());
  System.out.println("equals: "+a.equals(b));
  System.out.println(a==b);
  System.out.println(set+"  set.size= "+set.size());}

运行结果:
在这里插入图片描述
(61为97的16进制表示,toString()方法未重写,"@"后打印hashCode的16进制值

Set中仅存在一个对象,这样设计hashCode()方法,就遵循上述原则。即equals()为true的两个对象hashCode一定相同

最后贴一下Java API对于HashCode()方法的解释
HashCode()方法:返回对象的哈希码值。 这个方法是对于支持哈希表有益的,
*例如由提供的哈希表
* {@link java.util.HashMap}。每当在同一个对象上多次调用它时
*执行Java应用程序,{@code hashCode}方法
*必须始终返回相同的整数,前提是没有信息
*用于{@code equals}对象的比较被修改。
*这个整数不需要一次执行就保持一致
*应用程序到同一应用程序的另一个执行。
如果两个对象根据{@code equals(Object)}相等
*方法,然后在每个上调用{@code hashCode}方法
*这两个对象必须产生相同的整数结果。
如果两个对象不相等则不需要
*根据{@link java.lang.Object#equals(java.lang.Object)}
*方法,然后在每个上调用{@code hashCode}方法
*两个对象必须产生不同的整数结果。然而
*程序员应该意识到产生不同的整数结果
*对于不等对象可能会提高哈希表的性能。

  • 作者:whisperImp
  • 原文链接:https://blog.csdn.net/whispeImp/article/details/100734607
    更新时间:2022-10-11 09:27:45