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

本文深入探讨了hashCode()和equals()方法在Java中的实现与应用,通过实例解析了这两个方法的作用及如何影响HashSet容器中对象的唯一性判断。

hashCode(),equals() 示例讲解

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

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

public class Person {
 private String name;
 
 private Person(String name) {
  this.name = name;
 }
 
 @Override
 public int hashCode() {
  
  return 1;
 }
 
 @Override
 public boolean equals(Object obj) {
  if (obj instanceof Person) {
   Person p = (Person) obj;
   return this.name.equals(p.name);
  }
  return false;
 }

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

public static void main(String[] args) {
  String str = "a";
  Person a = new Person(str);
  Person b = new Person(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

我们添加几行代码

public static void main(String[] args) {
  String str = "a";
  Person a = new Person(str);
  Person b = new Person(str);
  HashSet<Person> set = new HashSet<>();
  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()方法

public class Person {
 private String name;
 
 public Person(String name) {
  this.name = name;
 }
 
// @Override
// public int hashCode() {
//  
//  return 1;
// }
 
 @Override
 public boolean equals(Object obj) {
  if (obj instanceof Person) {
   Person p = (Person) obj;
   return this.name.equals(p.name);
  }
  return false;
 }
 
 public static void main(String[] args) {
  String str = "a";
  Person a = new Person(str);
  Person b = new Person(str);
  HashSet<Person> set = new HashSet<>();
  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属性赋不同的值

public main(String[] args) {
  Person a = new Person("a");
  Person b = new Person("b");
  HashSet<Person> set = new HashSet<>();
  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即可。

@Override
 public int hashCode() {
  
  return name.hashCode();//返回属性name的hashCode
 }
@Override
 public boolean equals(Object obj) {
  if (obj instanceof Person) {
   Person p = (Person) obj;
   return this.name.equals(p.name);
  }
  return false;
 }
 public static void main(String[] args) {
  Person a = new Person("a");
  Person b = new Person("a");
  HashSet<Person> set = new HashSet<>();
  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}方法
*两个对象必须产生不同的整数结果。然而
*程序员应该意识到产生不同的整数结果
*对于不等对象可能会提高哈希表的性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值