覆写equals方法为什么需要覆写hashCode方法
覆写equals方法必须覆写hashCode方法,是JDK API上反复说明的,不过为什么要这样做呢?这两个方法之间有什么关系呢?
void test() {
// Person类的实例作为Map的key
Map<Person, Object> map = new HashMap<Person, Object>();
map.put(new Person("张三"), new Object());
// Person类的实例作为List的元素
List<Person> list = newArrayList<Person>()
list.add(new Person("张三"));
// 列表中是否包含
boolean b1 = list.contains(new Person("张三"));
// Map中是否包含
boolean b2 = map.containsKey(new Person("张三"));
System.out.println(b1 + "|" + b2);
}
Person类:
public class Person {
private String name;
private String age;
public Person(String name) {
setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
/*
* 使用getClass是为了防止子类被判断为父类的情况
* 例如:Person中覆写equals方法 {return name.equals(p.getName);}
* 子类Employee继承Person,此时子类中的euqals方法super.equlas(p);
* 此时如果使用instanceof关键字,因为Employee是Person的子类,所以返回true
* 进一步调用equals返回的是true,所以会将Person和Employee认为是同一人,显然不合理
*/
if(o != null && o.getClass() == this.getClass()) {
Person p = (Person) o;
if(p.getName() == null && this.name == null)
return false;
return this.name.equals(p.getName());
}
return false;
}
}
我们先来看b1,Person类的equals覆写了,不再判断两个地址是否相等,而是根据人员的姓名来判断两个对象是否相等,所以不管我们的 new Person(“张三”)产生了多少个对象,它们都是相等的。把“张三”对象放入List中,再检查List中是否包含,那结果肯定是true了。
接着来看b2,我们把张三这个对象作为了Map的键(Key),放进去的对象是张三,检查的对象还是张三,那应该和List的结果相同了,但是很遗憾,结果是false。原因何在呢?
原因就是HashMap的底层处理机制是以数组的方式保存Map条目(Map Entry)的,这其中的关键是这个数组下标的处理机制:依据传入元素hashCode方法的返回值决定其数组的下标,如果该数组位置上已经有了Map条目,且与传入的键值相等则不处理,若不相等则覆盖;如果数组位置没有条目,则插入,并加入到Map条目的链表中。同理,检查键是否存在也是根据哈希码确定位置,然后遍历查找键值的。
接着深入探讨,那对象元素的hashCode方法返回的是什么值呢?它是一个对象的哈希码,是由Object类的本地方法生成的,确保每个对象有一个哈希码(这也是哈希算法的基本要求:任意输入k,通过一定算法f(k),将其转换为非可逆的输出,对于两个输入k1和k2,要求若k1=k2,则必须 f(k1)=f(k2),但也允许k1≠k2,f(k1)=f(k2)的情况存在)。
那回到我们的例子上,由于我们没有重写hashCode方法,两个张三对象的hashCode方法返回值(也就是哈希码)肯定是不相同的了,在HashMap的数组中也就找不到对应的Map条目了,于是就返回了false。
问题清楚了,修改也非常简单,重写一下hashCode方法即可,代码如下:
public int hashCode() {
//其中HashCodeBuilder是org.apache.commons.lang.builder包下的一个哈希码生成工具,
//使用起来非常方便,诸位可以直接在项目中集成。
return new HashCodeBuilder().append(name).toHashCode();
}
其中HashCodeBuilder是org.apache.commons.lang.builder包下的一个哈希码生成工具,使用起来非常方便,诸位可以直接在项目中集成。(为什么不直接写hashCode方法?因为哈希码的生成有很多种算法,自己写麻烦,事儿又多,所以采用拿来主义是最好的方法。)
覆写equals方法为什么需要覆写hashCode方法的更多相关文章
- 第九条:覆盖equals方法时总要覆盖hashCode方法
Object类的hashCode方法: public native int hashCode(); 是一个本地方法. 其中这个方法的主要注释如下: Whenever it is invoked o ...
- 半夜思考, 为什么建议重写 equals() 方法时, 也要重写 hashCode() 方法
我说的半夜, 并不是真正的半夜, 指的是在我一个人的时候, 我会去思考一些奇怪的问题. 要理解 hashCode() 需要理解下面三个点: hash契约 哈希冲突 哈希可变 第一点: hash 契约指 ...
- JAVA中重写equals()方法的同时要重写hashcode()方法
object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true:注意:当此方法 ...
- 【Java实战】源码解析为什么覆盖equals方法时总要覆盖hashCode方法
1.背景知识 本文代码基于jdk1.8分析,<Java编程思想>中有如下描述: 另外再看下Object.java对hashCode()方法的说明: /** * Returns a hash ...
- 重写equals方法,也应该重写hashcode方法,反之亦然
yls 2019年11月07日 一方面 hashcode原则:两个对象equals相等,hashcode值一定相等 默认的hashcode是Object类通过对象的内存地址得到的 若重写equals而 ...
- 为什么在重写 equals 方法的时候需要重写 hashCode 方法?
因为有强制的规范指定需要同时重写 hashcode 与 equal 是方法,许多容器类, 如 HashMap.HashSet 都依赖于 hashcode 与 equals 的规定.
- Effective Java 第三版——11. 重写equals方法时同时也要重写hashcode方法
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- 浅谈Java中的hashcode方法以及equals方法
哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native int hashCode(); 根据这个 ...
- 为什么重写equals时必须重写hashCode方法?
原文地址:http://www.cnblogs.com/shenliang123/archive/2012/04/16/2452206.html 首先我们先来看下String类的源码:可以发现Stri ...
- Java中==、equals、hashcode的区别与重写equals以及hashcode方法实例(转)
Java中==.equals.hashcode的区别与重写equals以及hashcode方法实例 原文地址:http://www.cnblogs.com/luankun0214/p/4421770 ...
随机推荐
- bootstrap网站后台从设计到开发之登录
节后综合征缓了两天,今天把登录界面的主要后台代码分享给大家. protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPo ...
- python之线程相关操作(补充)
1 线程的其他方法 import threading import time from threading import Thread, current_thread def f1(n): time. ...
- npm install权限问题,报错:permission denied。
1.部署gulp项目时,nodeJs和gulp都已经正确安装,在项目内部执行npm install命令时,有些gulp的插件一直下载不成功,报错几种以下错误: “gulp-imagemin: Coul ...
- 腾讯Tars环境搭建 ---- centos
1,安装git yum install git 2,下载脚本 git clone https://github.com/tangramor/Tars_Install.git 注意:会有3个脚本,cen ...
- 使用Twitter异常检测框架遇到的坑
在Github上搜索“Anomaly Detection”,Twitter的异常检测框架(基于R语言)高居榜首,可见效果应该不错: 但是活跃度并不高,3-4年没人维护了: 因此在使用时难免会遇到一些坑 ...
- SoapUI 利用SoapUI进行简单的接口并发测试
利用SoapUI进行简单的接口并发测试 by:授客 QQ:1033553122 测试环境: SoapUI Pro 5.1.2 步骤如下 1. 把请求添加到测试套件 1.1. 途径1 1.新 ...
- SG Input 软件安全分析之逆向分析
前言 通过本文介绍怎么对一个 windows 程序进行安全分析.分析的软件版本为 2018-10-9 , 所有相关文件的链接 链接:https://pan.baidu.com/s/1l6BuuL-HP ...
- [20170627]使用TSPITR恢复表空间.txt
[20170627]使用TSPITR恢复表空间.txt --//RMAN提供了一种实现所谓TSPITR(Tablespace Point-In-Time Recovery)的技术,通过简单的一个语句, ...
- [20170617]vim中调用sqlplus.txt
[20170617]vim中调用sqlplus.txt --//以前写过一篇emacs下调用sqlplus的文章,一直想学emacs,受限制自己掌握vim,对学习它没有兴趣,原链接如下:--//htt ...
- Linux (Fedora 28) 如何查看 CPU 温度
机器突然负载有点高,查看CPU 温度的方法: 1.yum install lm_sensors; 2.sensors-detect 3.sensors 这些需要机器具备相应的设备,如果在虚拟机运行 ...