前言

在我们需要比较对象是否相等时,我们往往需要采取重写equals方法和hashcode方法。

该篇,就是从比较对象的场景结合通过代码实例以及部分源码解读,去跟大家品一品这个重写equals方法和hashcode方法。

正文 

场景:

我们现在需要比较两个对象  Pig 是否相等  。

而Pig 对象里面包含 三个字段, name,age,nickName  ,我们现在只需要认为如果两个pig对象的name名字和age年龄一样,那么这两个pig对象就是一样的,nickName昵称不影响相等的比较判断。

代码示例:

Pig.java:

/*** @Author : JCccc* @CreateTime : 2020/4/21* @Description :**/public class Pig {private  String name;private Integer age;private String nickName;public Pig() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getNickName() {return nickName;}public void setNickName(String nickName) {this.nickName = nickName;}}

可以看到上面的Pig对象,没有重写equals方法 和 hashcode 方法,那么我们去比较两个Pig对象,就算都设置了三个一样的属性字段,都是返回 false:

public static void main(String[] args) {Pig pig1=new Pig();pig1.setName("A");pig1.setAge(11);pig1.setNickName("a");String name= new String("A");Pig pig2=new Pig();pig2.setName(name);pig2.setAge(11);pig2.setNickName("B");System.out.println(pig1==pig2);  //falseSystem.out.println(pig1.equals(pig2)); //falseSystem.out.println(pig1.hashCode() ==pig2.hashCode()); //false}

为什么false? 很简单,因为pig1和pig2都是新new出来的,内存地址都是不一样的。

== : 比较内存地址  ,那肯定是false了 ;

equals: 默认调用的是Object的equals方法,看下面源码图,显然还是使用了== ,那就还是比较内存地址,那肯定是false了;

hashCode: 这是根据一定规则例如对象的存储地址,属性值等等映射出来的一个散列值,不同的对象存在可能相等的hashcode,但是概率非常小(两个对象equals返回true时,hashCode返回肯定是true;而两个对象hashCode返回true时,这两个对象的equals不一定返回true;  还有,如果两个对象的hashCode不一样,那么这两个对象一定不相等!)。
一个好的散列算法,我们肯定是尽可能让不同对象的hashcode也不同,相同的对象hashcode也相同。这也是为什么我们比较对象重写equals方法后还会一起重写hashcode方法。接下来会有介绍到。

好的,上面啰嗦了很多,接下来我们开始去重写equals方法和hashCode方法,实现我们这个Pig对象的比较,只要能保证name和age两个字段属性一致,就返回相等true。

首先是重写equals方法(看上去我似乎写的很啰嗦吧,我觉得这样去写更容易帮助新手去理解):

@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Pig pig = (Pig) o;boolean nameCheck=false;boolean ageCheck=false;if (this.name == pig.name) {nameCheck = true;} else if (this.name != null && this.name.equals(pig.name)) {nameCheck = true;}if (this.age == pig.age) {ageCheck = true;} else if (this.age != null && this.age.equals(pig.age)) {ageCheck = true;}if (nameCheck && ageCheck){return true;}return  false;}

稍微对重写的代码做下解读,请看图:

事不宜迟,我们在重写了equals后,我们再比较下两个Pig对象:

可以看到,到这里好像已经能符合我们的比较对象逻辑了,但是我们还需要重写hashCode方法。

为什么?

原因1. 

通用约定,

翻译:

/*请注意,通常需要重写{@code hashCode}*方法,以便维护*{@code hashCode}方法的常规约定,它声明*相等的对象必须有相等的哈希码。*/

原因2.

为了我们使用hashmap存储对象 (下面有介绍)

没错,就是文章开头我们讲到的,相同的对象的hashCode 的散列值最好保持相等, 而不同对象的散列值,我们也使其保持不相等。

而目前我们已经重写了equals方法,可以看到,只要两个pig对象的name和age都相等,那么我们的pig的equals就返回true了,也就是说,此时此刻,我们也必须使两个pig的hashCode 的散列值保持相等,这样才是对象相等的结果。

事不宜迟,我们继续重写hashCode方法:

    @Overridepublic int hashCode() {int result = 17;result = 31 * result + name.hashCode();result = 31 * result + age;return result;}

然后我们再比较下两个pig对象:

也就是说,最终我们重写了equals和hashCode方法后, Pig.java:

/*** @Author : JCccc* @CreateTime : 2020/4/21* @Description :**/public class Pig {private  String name;private Integer age;private String nickName;public Pig() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public String getNickName() {return nickName;}public void setNickName(String nickName) {this.nickName = nickName;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Pig pig = (Pig) o;boolean nameCheck=false;boolean ageCheck=false;if (this.name == pig.name) {nameCheck = true;} else if (this.name != null && this.name.equals(pig.name)) {nameCheck = true;}if (this.age == pig.age) {ageCheck = true;} else if (this.age != null && this.age.equals(pig.age)) {ageCheck = true;}if (nameCheck && ageCheck){return true;}return  false;}@Overridepublic int hashCode() {int result = 17;result = 31 * result + name.hashCode();result = 31 * result + age;return result;}
}

看到这里,应该有不少人觉得,重写怎么有点麻烦,有没有简单点的模板形式的?

有的,其实在java 7 有在Objects里面新增了我们需要重新的这两个方法,所以我们重写equals和hashCode还可以使用java自带的Objects,如:

    @Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Pig pig = (Pig) o;return Objects.equals(name, pig.name) &&Objects.equals(age, pig.age);}@Overridepublic int hashCode() {return Objects.hash(name, age);}

那么如果还是觉得有点麻烦呢? 

那就使用lombok的注解,让它帮我们写,我们自己就写个注解!

导入lombok依赖:

        <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.10</version></dependency>

然后在Pig类上,使用注解:

然后编译后,可以看到lombok帮我们重写了equals和hashcode方法:

其实如果咱们使用到了lombok的话,用@Data注解应该基本就够用了,它结合了@ToString,@EqualsAndHashCode,@Getter和@Setter的功能。

ps:再啰嗦几句, 我们自己重写了hashCode方法后,能够确保两个对象返回的的hashCode散列值是不一样的,这样一来,

在我们使用hashmap 去存储对象, 在进行验重逻辑的时候,咱们的性能就特别好了。

为啥说这样性能好, 我们再啰嗦地补充一下hashmap在插入值时对key(这里key是对象)的验重:

 HashMap中的比较key:
先求出key的hashcode(),比较其值是否相等;
-若相等再比较equals(),若相等则认为他们是相等 的。
-若equals()不相等则认为他们不相等。
如果只重写hashcode()不重写equals()方法,当比较equals()时只是看他们是否为 同一对象(即进行内存地址的比较),所以必定要两个方法一起重写。

而HashSet,
用来判断key是否相等的方法,其实也是调用了HashMap加入元素方法,再判断是否相等。

好的,该篇介绍就到此吧。

ps:其实没有结束,我还想啰嗦一下,因为我们在重写hashcode方法的时候,我们看到了一个数字 31 。 想必会有很多人奇怪,为什么要写个31啊?

这里引用一下《Effective Java》 里面的解释:

之所以使用 31, 是因为他是一个奇素数。
如果乘数是偶数,并且乘法溢出的话,信息就会丢失,因为与2相乘等价于移位运算(低位补0)。
使用素数的好处并不很明显,但是习惯上使用素数来计算散列结果。
31 有个很好的性能,即用移位和减法来代替乘法,
可以得到更好的性能: 31 * i == (i << 5)- i,
现代的 VM 可以自动完成这种优化。这个公式可以很简单的推导出来。

那么可能还有眼尖的人看到了,为什么还要个数字17?

这个其实道理一样,在《Effective Java》里,作者推荐使用的就是 基于17和31的散列码的算法 ,而在Objects里面的hash方法里,17换做了1 。

好吧,这篇就真的到此结束吧。

Java 细品 重写equals方法 和 hashcode 方法相关推荐

  1. HashSet要重写equals方法和hashCode方法

    注:重要笔记在代码中注释有 hashSet去重: 即判断两个对象是否相等 1:会先调用对象的hashCode方法获得hash的值,如果set中哈希表里面没有对应的hash值,则将次对象存入set中 2 ...

  2. java重写6,java重写equals()方法和hashCode()方法

    1.equals()方法和hashCode()方法是什么? equals()和hashCode()都是是Java中万物之源Object类中的方法: equals方法用于比较两个对象是否相同,Objec ...

  3. JAVA正确地自定义比较对象---如何重写equals方法和hashCode方法

    在实际应用中经常会比较两个对象是否相等,比如下面的Address类,它有两个属性:String province 和 String city. public class Address {privat ...

  4. java中重写hashcode_Java中HashSet要重写equals方法和hashCode方法

    下面给出一个属性不同但哈希码相同的例子: import java.util.HashSet; import java.util.Iterator; import java.util.Set; clas ...

  5. Java基础语法:重写equals方法和hasCode方法

    目录 一.为什么需要重写equals和hasCode方法 二.equals和hasCode解读 1.equals方法 2.hasCode方法 三.重写 四.参考资料 一.为什么需要重写equals和h ...

  6. 重写equals()时为什么也得重写hashCode()之深度解读equals方法与hashCode方法渊源

    重写equals()时为什么也得重写hashCode()之深度解读equals方法与hashCode方法渊源 在使用Map接口时,我们的愿望是当key1.equals(key2)时,它们获取的valu ...

  7. 详解 equals() 方法和 hashCode() 方法

    来源:编程迷思, www.cnblogs.com/kismetv/p/7191736.html 前言 Java的基类Object提供了一些方法,其中equals()方法用于判断两个对象是否相等,has ...

  8. equals方法和hashCode方法之间的那些事(1.1)

    我们先来看一下java官方 jdk中关于equals方法和hashCode方法的介绍: 不知道读者们注意到一个细节没有,就是: 注意:当此方法被重写时,通常有必要重写 hashCode 方法,以维护 ...

  9. 重写equal()时为什么也得重写hashCode()之深度解读equal方法与hashCode方法渊源

    转载请注明出处: http://blog.csdn.net/javazejian/article/details/51348320 今天这篇文章我们打算来深度解读一下equal方法以及其关联方法has ...

最新文章

  1. 用python画小猪佩奇(非原创)
  2. 程序员编程10大原则,请牢牢记住
  3. linux系统添加新用户并赋予相应权限
  4. vue.js框架原理浅析 1
  5. 《推荐系统实践》样章:如何利用用户标签数据
  6. html5储存类型,html5本地存储-留言板
  7. php判断中英文请求,并实现跳转
  8. java api 第一个类是_JAVA常用API:String 类的常用方法
  9. CISCO ASA 5510 防火墙的配置实例
  10. 矩阵问题入门(矩阵乘法and矩阵快速幂)acm寒假集训日记22/1/15
  11. 计算机网络(9)-----TCP可靠传输的实现
  12. js版俄罗斯方块(二)
  13. 持续集成jenkins工具介绍(一)
  14. linux usb有线网卡驱动_求助!linux下安装usb无线网卡驱动成功 但是ping不通网关。。。...
  15. Dell T40服务器系统安装问题
  16. 单片机 TDA8023 读 ic 卡 smrat card sync_card
  17. 程序员为啥更赚钱?用Python做副业增长上万,躺赚
  18. 微信公众号开发,移动端开发遇到的问题及其他技巧
  19. nginx学习,看这一篇就够了:下载、安装。使用:正向代理、反向代理、负载均衡。常用命令和配置文件,很全
  20. Android 面试系列(一)Android 基础

热门文章

  1. python车牌定位
  2. Linux GIC代码分析
  3. 求助:hmailserver+roundcube启用密码插件后,用户无法修改密码问题
  4. HashMap为什么是2倍扩容
  5. 查看windows系统默认编码 修改windows系统默认编码
  6. 穆易天气app代码(一)
  7. 低年级学生必读——牛人的大学四年是这样过的
  8. 5.文献研读---基于教育数据挖掘的在线学习者 学业成绩预测建模研究--陈子健 朱晓亮...
  9. 使用python和opencv进行人脸识别时遇到cv2.error: OpenCV(4.5.1) C:\Users\appveyor\AppData\Local\Temp\1\pip-req-buil
  10. Qt美化之基础控件美化