您的位置 首页 java

Java为什么要重写equals方法和hashCode方法

重写equals()方法

为什么重写equals方法?我们先来看看Object类是如何实现equals()方法的:

Object类的equals方法

就是这么简单。根据Object类默认实现的equals方法, 类的每个实例都只于它自身相等 。这在一定情况下是合适的实现,比如类的每个实例本质上都是唯一的,像Thread类、Requests类等。但在更多的情况下这个equals方法并不合适。比如下面这种情况:

public class MyClass {
 public static void main(String[] args) {
 Person p1 = new Person(" 特朗普 ","123456789");
 Person p2 = new Person("特朗普","123456789");
 
 System.out.println(p1.equals(p2));
 }
}
class Person{
 String name;
 String idCard;
 Person(String name, String idCard){
 this.name = name;
 this.idCard = idCard;
 }
 public String getName(){ return this.name; }
 public String getIdCard(){ return this.idCard; }
}
 

上面的代码输出的是false。按我们的想法,一个人的名字和身份证号唯一可以确定一个人,所以这里p1和p2应该在逻辑上相等。但如图所示,因为Object默认equals方法的原因,返回的是false。一般类似这种情况,我们需要重写equals方法。

我们为上面例子的类重写equals方法:

class Person{
 ...
 @Override
 public boolean equals(Object o){
 if(o instanceof Person){
 Person obs = (Person)o;
 if(obs.getName().equals(this.name) && obs.getIdCard().equals(this.idCard)){
 return true;
 }
 return false;
 }
 return false;
 }
 ...
}
 

我们只需要在Person类中“添加”(重写)这个equals方法,那么程序就会按照我们的意愿输出true。因为我们的想法是身份证号和姓名一致就是同一个人,所以我们在equals方法里面判断如果入参和自身的这两个字段值相等,我们就返回true即可。

equals方法我们可以理解为数学上的=号,那么在实现equals方法时,我们可以类比=号保证equals必须满足下面几个条件:

  • 自反性:如果x对象不为null,那么x.equals(x)必须保证返回true
  • 对称性:如果x和y非空,那么x.equals(y)返回true时,y.equals(x)也必须保证返回true
  • 传递性:如果x、y、z非空,那么当x.equals(y)和y.equals(z)返回true时,x.equals(z)也必须保证返回true
  • 一致性:x和y非空,且信息没有被修改过,那么不同时间段x.equals(y)返回的值必须要相同
  • null规定:如果x非空,x.equals(null)必须保证返回false

上面这些其实很好理解,比如如果我们实现的equals方法不满足自反性,那么所有有关去重或者查找的方法将可能不起作用。

为了引出下一个标题,这里举一个例子:

Set里放Person对象

我们已经为Person添加了equals方法,但这段代码还是会返回false,并不是true。为什么?

我先说答案:因为没有实现 hashCode ()方法。我们看HashSet:

HashSet是用hashMap保存数据的

HashSet内部是用HashMap保存数据的。

hashSet的contains方法

HashSet的contains方法是直接调用map的containsKey方法实现的。

HashMap的containsKey方法

containsKey中的hash()方法需要调用入参对象的hashCode()方法。

HashMap hash方法

到这里,不需要再进入getNode方法,相信大家也知道原因了。

重写hashCode()方法

我们都知道:重写equals方法后一定要重写hashCode方法。为什么呢?我们来看看Object源码:

jdk8 中的 hashCode()

图中hashCode(规范总结一下就是三点:

  1. 在应用程序执行期间,如果对象equals方法比较操作用到的信息没有被修改,那么对同一个对象调用多次,hashCode方法必须保证返回同一个整数。用上面的Person例子,只要p1对象的name和idCard没有修改,那么多次调用p1的hashCode必须一致返回同一个值。但这里有一个例外,如果应用程序重启了,那么重启后对象hashCode返回的值可以和重启前不一样。
  2. 如果两个对象通过equals方法比较返回true,那么这两个对象的hashCode也必须返回同一个整数。其实就一句话:相等的对象必须具有一致的散列码。否则当该类的对象需要用到基于散列的操作时,就会出问题。
  3. 如果两个对象通过equals方法比较返回false,它们的hashCode可以返回同一个整数。其实这个就是hash冲突。虽然不相等的对象可以返回同一个散列值,但减少这种情况有利于提高 散列表 的性能。

重写hashCode()方法很简单,@Override public int hashCode(){return 1;}这就实现了重写hashCode,它把所有对象散列为1.当然这种做法是超级烂的,只是用来说明重写hashCode的方法。重写hashCode涉及到hash算法,这个相信了解过数据结构的人都知道,什么数字分析法、折叠法、平方取中法等等。都学过的。

String实现的equals和hashCode方法

最后拿String对象来学习一下规范的equals方法和hashCode方法如何写:

String 的equals方法

我在 总结String是不可变对象时,提到String的属性中除了hash属性外都是final的。这是为什么?这涉及到一个优化思路:如果一个对象是不可变的,且计算散列码消耗比较大,那么应该把对象的散列码缓存到对象内部,而并非每次都重新计算。

String 的hashCode方法

文章来源:智云一二三科技

文章标题:Java为什么要重写equals方法和hashCode方法

文章地址:https://www.zhihuclub.com/187022.shtml

关于作者: 智云科技

热门文章

网站地图