重写hashcode和equals方法

一。前言

我们都知道,要比较两个对象是否相等时需要调用对象的equals()方法,即判断对象引用所指向的对象地址是否相等,对象地址相等时,那么与对象相关的对象句柄、对象头、对象实例数据、对象类型数据等也是完全一致的,所以我们可以通过比较对象的地址来判断是否相等。

二。Object源码理解

对象在不重写的情况下使用的是Object的equals方法和hashcode方法,从Object类的源码我们知道,默认的equals 判断的是两个对象的引用指向的是不是同一个对象;而hashcode也是根据对象地址生成一个整数数值;

另外我们可以看到Object的hashcode()方法的修饰符为native,表明该方法是否操作系统实现,java调用操作系统底层代码获取哈希值。

public class Object {public native int hashCode();/*** Indicates whether some other object is "equal to" this one.* <p>* The {@code equals} method implements an equivalence relation* on non-null object references:* <ul>* <li>It is <i>reflexive</i>: for any non-null reference value*     {@code x}, {@code x.equals(x)} should return*     {@code true}.* <li>It is <i>symmetric</i>: for any non-null reference values*     {@code x} and {@code y}, {@code x.equals(y)}*     should return {@code true} if and only if*     {@code y.equals(x)} returns {@code true}.* <li>It is <i>transitive</i>: for any non-null reference values*     {@code x}, {@code y}, and {@code z}, if*     {@code x.equals(y)} returns {@code true} and*     {@code y.equals(z)} returns {@code true}, then*     {@code x.equals(z)} should return {@code true}.* <li>It is <i>consistent</i>: for any non-null reference values*     {@code x} and {@code y}, multiple invocations of*     {@code x.equals(y)} consistently return {@code true}*     or consistently return {@code false}, provided no*     information used in {@code equals} comparisons on the*     objects is modified.* <li>For any non-null reference value {@code x},*     {@code x.equals(null)} should return {@code false}.* </ul>* <p>* The {@code equals} method for class {@code Object} implements* the most discriminating possible equivalence relation on objects;* that is, for any non-null reference values {@code x} and* {@code y}, this method returns {@code true} if and only* if {@code x} and {@code y} refer to the same object* ({@code x == y} has the value {@code true}).* <p>* Note that it is generally necessary to override the {@code hashCode}* method whenever this method is overridden, so as to maintain the* general contract for the {@code hashCode} method, which states* that equal objects must have equal hash codes.** @param   obj   the reference object with which to compare.* @return  {@code true} if this object is the same as the obj*          argument; {@code false} otherwise.* @see     #hashCode()* @see     java.util.HashMap*/public boolean equals(Object obj) {return (this == obj);}
}

三。需要重写equals()的场景

假设现在有很多学生对象,默认情况下,要判断多个学生对象是否相等,需要根据地址判断,若对象地址相等,那么对象的实例数据一定是一样的,但现在我们规定:当学生的姓名、年龄、性别相等时,认为学生对象是相等的,不一定需要对象地址完全相同,例如学生A对象所在地址为100,学生A的个人信息为(姓名:A,性别:女,年龄:18,住址:北京软件路999号,体重:48),学生A对象所在地址为388,学生A的个人信息为(姓名:A,性别:女,年龄:18,住址:广州暴富路888号,体重:55),这时候如果不重写Object的equals方法,那么返回的一定是false不相等,这个时候就需要我们根据自己的需求重写equals()方法了。

package jianlejun.study;public class Student {private String name;// 姓名private String sex;// 性别private String age;// 年龄private float weight;// 体重private String addr;// 地址// 重写hashcode方法@Overridepublic int hashCode() {int result = name.hashCode();result = 17 * result + sex.hashCode();result = 17 * result + age.hashCode();return result;}// 重写equals方法@Overridepublic boolean equals(Object obj) {if(!(obj instanceof Student)) {// instanceof 已经处理了obj = null的情况return false;}Student stuObj = (Student) obj;// 地址相等if (this == stuObj) {return true;}// 如果两个对象姓名、年龄、性别相等,我们认为两个对象相等if (stuObj.name.equals(this.name) && stuObj.sex.equals(this.sex) && stuObj.age.equals(this.age)) {return true;} else {return false;}}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}public float getWeight() {return weight;}public void setWeight(float weight) {this.weight = weight;}public String getAddr() {return addr;}public void setAddr(String addr) {this.addr = addr;}}

现在我们写个例子测试下结果:

public static void main(String[] args) {Student s1 =new Student();s1.setAddr("1111");s1.setAge("20");s1.setName("allan");s1.setSex("male");s1.setWeight(60f);Student s2 =new Student();s2.setAddr("222");s2.setAge("20");s2.setName("allan");s2.setSex("male");s2.setWeight(70f);if(s1.equals(s2)) {System.out.println("s1==s2");}else {System.out.println("s1 != s2");}
}

在重写了student的equals方法后,这里会输出s1 == s2,实现了我们的需求,如果没有重写equals方法,那么上段代码必定输出s1!=s2。

通过上面的例子,你是不是会想,不是说要同时重写Object的equals方法和hashcode方法吗?那上面的例子怎么才只用到equals方法呢,hashcode方法没有体现出来,不要着急,我们往下看。

四。需要重写hashcode()的场景

以上面例子为基础,即student1和student2在重写equals方法后被认为是相等的。

在两个对象equals的情况下进行把他们分别放入Map和Set中

在上面的代码基础上追加如下代码:

Set set = new HashSet();set.add(s1);set.add(s2);System.out.println(set);

如果没有重写Object的hashcode()方法(即去掉上面student类中hashcode方法块),这里会输出

[jianlejun.study.Student@7852e922, jianlejun.study.Student@4e25154f]

说明该Set容器类有2个元素。…等等,为什么会有2个元素????刚才经过测试,s1不是已经等于s2了吗,那按照Set容器的特性会有一个去重操作,那为什么现在会有2个元素。这就涉及到Set的底层实现问题了,这里简单介绍下就是HashSet的底层是通过HashMap实现的,最终比较set容器内元素是否相等是通过比较对象的hashcode来判断的。现在你可以试试吧刚才注释掉的hashcode方法弄回去,然后重新运行,看是不是很神奇的就只输出一个元素了

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

或许你会有一个疑问?hashcode里的代码该怎么理解?该如何写?其实有个相对固定的写法,先整理出你判断对象相等的属性,然后取一个尽可能小的正整数(尽可能小时怕最终得到的结果超出了整型int的取数范围),这里我取了17,(好像在JDK源码中哪里看过用的是17),然后计算17*属性的hashcode+其他属性的hashcode,重复步骤。

重写hashcode方法后输出的结果为:

[jianlejun.study.Student@43c2ce69]

同理,可以测试下放入HashMap中,key为<s1,s1>,<s2,s2>,Map也把两个同样的对象当成了不同的Key(Map的Key是不允许重复的,相同Key会覆盖)那么没有重写的情况下map中也会有2个元素,重写的情况会最后put进的元素会覆盖前面的value

Map m = new HashMap();m.put(s1, s1);m.put(s2, s2);System.out.println(m);System.out.println(((Student)m.get(s1)).getAddr());输出结果:
{jianlejun.study.Student@43c2ce69=jianlejun.study.Student@43c2ce69}
222

可以看到最终输出的地址信息为222,222是s2成员变量addr的值,很明天,s2已经替换了map中key为s1的value值,最终的结果是map<s1,s2>。即key为s1value为s2.

五。原理分析

因为我们没有重写父类(Object)的hashcode方法,Object的hashcode方法会根据两个对象的地址生成对相应的hashcode;

s1和s2是分别new出来的,那么他们的地址肯定是不一样的,自然hashcode值也会不一样。

Set区别对象是不是唯一的标准是,两个对象hashcode是不是一样,再判定两个对象是否equals;

Map 是先根据Key值的hashcode分配和获取对象保存数组下标的,然后再根据equals区分唯一值(详见下面的map分析)

六。补充HashMap知识

hashMap组成结构:hashMap是由数组和链表组成;

hashMap的存储:一个对象存储到hashMap中的位置是由其key 的hashcode值决定的;查hashMap查找key: 找key的时候hashMap会先根据key值的hashcode经过取余算法定位其所在数组的位置,再根据key的equals方法匹配相同key值获取对应相应的对象;

案例:

(1)hashmap存储

存值规则:把Key的hashCode 与HashMap的容量 取余得出该Key存储在数组所在位置的下标(源码定位Key存储在数组的哪个位置是以hashCode & (HashMap容量-1)算法得出)这里为方便理解使用此方式;

//为了演示方便定义一个容量大小为3的hashMap(其默认为16)

HashMap map=newHashMap(3);

map.put(“a”,1); 得到key 为“a” 的hashcode 值为97然后根据 该值和hashMap 容量取余97%3得到存储位到数组下标为1;

map.put(“b”,2); 得到key 为“b” 的hashcode 值为98,98%3到存储位到数组下标为2;

map.put(“c”,3); 得到key 为“c” 的hashcode 值为99,99%3到存储位到数组下标为0;

map.put(“d”,4); 得到key 为“d” 的hashcode 值为100,100%3到存储位到数组下标为1;

map.put(“e”,5); 得到key 为“e” 的hashcode 值为101,101%3到存储位到数组下标为2;

map.put(“f”,6); 得到key 为“f” 的hashcode 值为102,102%3到存储位到数组下标为0;

在这里插入图片描述
(2)hashmap的查找key

得到key在数组中的位置:根据上图,当我们获取key 为“a”的对象时,那么我们首先获得 key的hashcode97%3得到存储位到数组下标为1;

匹配得到对应key值对象:得到数组下表为1的数据“a”和“c”对象, 然后再根据 key.equals()来匹配获取对应key的数据对象;

hashcode 对于HashMapde:如果没有hashcode 就意味着HashMap存储的时候是没有规律可寻的,那么每当我们map.get()方法的时候,就要把map里面的对象一一拿出来进行equals匹配,这样效率是不是会超级慢;

hashcode方法文档说明

在equals方法没被修改的前提下,多次调用同一对象的hashcode方法返回的值必须是相同的整数;

如果两个对象互相equals,那么这两个对象的hashcode值必须相等;

为不同对象生成不同的hashcode可以提升哈希表的性能;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/557781.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux 安装log4j,Log4j 安装

Log4j教程 - Log4j安装Log4j API包是根据Apache软件许可证分发的。最新的log4j版本&#xff0c;包括全源代码&#xff0c;类文件和文档可以在http://logging.apache.org/log4j/找到。我们可以从上面的链接下载apache-log4j-x.x.x.tar.gz或zip文件。支持库我们可以使用log4j将信…

input输入框只读的几种方式

input输入框只读的几种方式 **readonly&#xff1a;**只针对input(text / password)和textarea有效&#xff1b;如果设为true&#xff0c;用户只是不能编辑对应的文本&#xff0c;但是仍然可以聚焦焦点&#xff0c;并且在提交表单的时候&#xff0c;该输入项会作为form的一项提…

linux sql 语句菜鸟,Linux安装mysql

要在centos上安装mysql&#xff0c;这些知识还不是很了解&#xff0c;找了一些资料分享一下1、下载mysql-5.5.3-m3.tar.gz&#xff0c;并且解压.tar -xzvf mysql-5.1.36.tar.gz2、添加mysql组&#xff0c;新建mysql用户groupadd mysqluseradd -g mysql mysql3、进入目录cd mysq…

@Autowired注入为null的几种情况

1.在应用的Filter或Listener中使用了Autowired &#xff0c; 原因&#xff1a;因为Filter和Listener加载顺序优先于spring容器初始化实例&#xff0c;所以使用Autowired肯定为null了~~ 解决&#xff1a;用ApplicationContext根据bean名称&#xff08;注意名称为实现类而不是接…

约瑟夫环c语言程序完整版,C语言:约瑟夫环问题(源代码)

本帖最后由 geige 于 2015-7-26 00:48 编辑#include#includestruct stu //构建一个新的数据类型{int num;struct stu *next;};struct stu *createlist(int n); //函数声明int main(){struct stu *head,*p,*temp; //定义头指针&#xff0c;以及中间指针temp&#xff0c;用来删…

js中遇到的一个错误Uncaught SyntaxError: missing )after argument list

报的错误如下&#xff1a; 代码&#xff1a; 解决&#xff1a; 加上引号即可。

c语言第六次实验报告,第一年C语言实验报告6列

成都理工大学计算机工程学院“编程基础”实验报告1. 实验目的(1)精通一维数组. 二维数组的定义&#xff0c;初始化以及输入和输出方法(2)掌握字符数组和字符串函数的使用(3)掌握与数组有关的常用算法2. 实验内容(1)在给定的字符串中找到指定的字符&#xff1b;要求:①通过直接初…

静态页面被拦截解决办法

在配置中央调度器的时候&#xff0c;url-pattern设置为*.do&#xff0c;以后的请求中写什么.do&#xff0c;不会出现静态页面被拦截的情况。 当url-pattern设置为/时&#xff0c;出现了静态页面被拦截 解决&#xff1a; 1.使用defaultServlet&#xff0c;在web.xml中的Dispatch…

c语言程序设计报告数制转换,数制转换-C语言程序设计课程设计报告.docx

计算机学院《高级语言程序设计》课程设计报告 PAGE \* MERGEFORMAT 10学号2016-2017学年 第二学期《C语言程序设计》课程设计报告题目&#xff1a;数制转换专业&#xff1a;网络工程(对口)班级&#xff1a;姓名&#xff1a;指导教师&#xff1a;成绩&#xff1a;计算机学院2017…

IDEA中maven的Plugins报红解决方法

IDEA中maven的Plugins报红解决方法 Idea中maven的Plugins报红解决方法 我的maven项目中plugins下的所有文件都报红&#xff0c;查看报错&#xff0c;这是Maven中plugins没有存入本地的仓库&#xff0c;点击Settings查看Build&#xff0c;Execution…》》Build Tools》》Maven》…

ssm前端时间格式转换

1&#xff1a;在实体类加注解 DateTimeFormat(pattern"yyyy-MM-dd") private Date stime ;2&#xff1a;在前端页面引入jstl <% taglib uri"http://java.sun.com/jsp/jstl/fmt" prefix"fmt"%><table border"1px"><t…

标准C语言各种时间函数用法,使用c语言标准库中的时间函数

#include #include int main(void){time_t start, finish, now;struct tm *ptr;char *c, buf1[80];double duration;//记录程序开始的时间start time(0);//记录当前时间&#xff0c;以另一种方式调用time()time(&now);//将time_t值转换成tm类型的结构ptr localtime(&…

git credential manager for windows解决方法

换电脑之后因为git 账号问题修改了git账号密码&#xff0c;但问题出现了&#xff0c;每次都让输入账号密码&#xff0c;window的一遍&#xff0c;git的一遍。之后git设置可以保存密码&#xff0c;但window安全中心却每次都让输入密码解决方法。 一&#xff0c;保存 git 的密码…

冒泡排序c语言子程序,C语言之冒泡排序算法

一、搭建Zookeeper集群Zookeeper是一个分布式开源框架&#xff0c;提供了协调分布式应用的基本服务&#xff0c;它向外部应用暴露一组通用服务——分布式同步(DistributedSynchronization)、命名服务(NamingService)、集群维护(GroupMaintenance)等&#xff0c;简化分布式应用协…

sql语句中大于号、小于号、大于等于、小于等于号的写法

sql语句中大于号、小于号、大于等于、小于等于号的写法 第一种&#xff1a; <: < <: < >: > >: > sql如下&#xff1a; create_at > #{startTime} and create_at < #{endTime} 第二种&#xff1a; 大于等于: <![CDATA[ > ]]> 小于…

C语言挂载文件夹,使用autofs 按需挂载共享目录

23.6.1安装autofs通过如下命令检测autofs是否已经安装#rpm –q autofsautofs-5.0.1-0.rc2.88如果系统当前未安装autofs服务&#xff0c;可以通过系统安装光盘进行安装。23.6.2启动autofs服务#service autofs start关闭服务#service autofs stop查看autofs服务状态#service auto…

JavaScript 中的 window onload 应该什么时候写

JavaScript 中的 window onload 应该什么时候写 1. 页内式 JS 代码 1.1 页内式 JS 代码写在 head 内部 如果 script 标签写在 head 标签内部&#xff0c;则位于 body 内的元素将晚于 JS 代码加载&#xff0c;那么其中一些获取 DOM 元素的方法将无法取得元素&#xff08;返回 …

android歌词效果,自定义View:Android歌词控件

TicktockMusic 音乐播放器项目相关文章汇总&#xff1a;简介之前做 TicktockMusic 音乐播放器&#xff0c;一个必要的需求肯定是歌词&#xff0c;在 github 上找了几个&#xff0c;发现或多或少都有点不满足需求&#xff0c;所以就自己动手写了一个&#xff0c;本篇文章主要介绍…

IDEA项目中 target 目录的作用

IDEA项目中 target 目录的作用 target是idea默认的编译路径&#xff0c;用来存放项目的&#xff1a;文件和目录、jar包、war包、class文件等。

java上传图片到target目录,jsp如何访问target里的图片路径

java上传图片到target目录&#xff0c;jsp如何访问target里的图片路径 昨天有个同学在做springmvc的图片上传&#xff0c;上传成功了却无法从网页中访问&#xff0c;我看了看上传后的路径&#xff0c;它用的idea编辑器&#xff0c;图片上传到了target里的目录&#xff0c;src目…