一文告诉你为什么要重写hashCode()方法和equals()方法
作者:砥砺-前行 发布时间:2021-09-05 07:05:55
首先我们看下object源码中如何定义hashcode与equals方法的
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
Object类中的hashCode()方法,用的是native关键字修饰,说明这个方法是个原生函数,也就说这个方法的实现不是用java语言实现的,是使用c/c++实现的,并且被编译成了DLL,由java去调用,jdk源码中不包含。
Java将调用本地方法库对此方法的实现。由于Object类中有JNI方法调用,按照JNI的规则,应当生成JNI 的头文件,在此目录下执行 javah -jni java.lang.Object 指令,将生成一个 java_lang_Object.h 头文件
/*
* Class: java_lang_Object
* Method: hashCode
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_java_lang_Object_hashCode
(JNIEnv *, jobject);
具体在c中怎么实现我也不是很清楚
但是为什么要重写equals与hashcode呢,看个例子
先定义一个实体类
package org;
public class Chengxuyuan {
private Integer age;
private String company;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getCompany() {
return company;
}
public void setCompany(String company) {
this.company = company;
}
@Override
public String toString() {
return "Chengxuyuan{" +
"age=" + age +
", company='" + company + '\'' +
'}';
}
// @Override
// public int hashCode() {
// int hashcode = age.hashCode();
// hashcode = 31 * hashcode + company.hashCode();
// return hashcode;
// }
//
// @Override
// public boolean equals(Object obj) {
// if(!(obj instanceof Chengxuyuan)) {//首先要判断是不是同一个类型
// return false;
// }
// Chengxuyuan chengxuyuan = (Chengxuyuan) obj;
// if (this == chengxuyuan) {// 其次要判断地址是否相同相等
// return true;
// }
// if (chengxuyuan.age.equals(this.age) && chengxuyuan.company.equals(this.company)) {// 最后要判断对象里的属性是否相同
// return true;
// } else {
// return false;
// }
//}
}
然后通过对map的操作来查看结果
package org;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) {
Map<Chengxuyuan, String> map = new HashMap<>();
Chengxuyuan chengxuyuan = new Chengxuyuan();
chengxuyuan.setAge(15);
chengxuyuan.setCompany("没公司");
System.out.println(chengxuyuan.hashCode());
map.put(chengxuyuan, chengxuyuan.getCompany());
Chengxuyuan chengxuyuan2 = new Chengxuyuan();
chengxuyuan2.setAge(15);
chengxuyuan2.setCompany("没公司");
System.out.println(chengxuyuan2.hashCode());
map.put(chengxuyuan2, chengxuyuan2.getCompany());
System.out.println(chengxuyuan.equals(chengxuyuan2));
System.out.println(map);
System.out.println(map.get(chengxuyuan2));
}
}
查看结果
824318946
930990596
false
{Chengxuyuan{age=15, company='没公司'}=没公司, Chengxuyuan{age=15, company='没公司'}=没公司}
没公司
从上述内容可以看到 同样内容保存到map中本应该是一条内容,但是现在是两条信息,在map中保存数据,首先hashmap在保存数据的时候会会计算key的hashcode来作为key的键值来保存信息
hashmap源码
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
所以当key得hashcode值没有被重写的话两个对象不是是无法相等的,所以首先要重写hashcode
重写hashcode
/**
* 根据指定数组的内容返回哈希码。如果该数组包含其他数组作为元素,
* 则哈希码基于其标识而不是其内容。因此,可以直接或间接通过一个
* 或多个级别的数组在包含自身作为元素的数组上调用此方法。 <p>对
* 于任何两个数组<tt> a <tt>和<tt> b <tt>,例如<tt> Arrays.equals
* (a,b)<tt>,<tt> Arrays也是这种情况。 hashCode(a)==
* Arrays.hashCode(b)<tt>。 <p>此方法返回的值等于<tt>
* Arrays.asList(a).hashCode()<tt>返回的值,除非<tt> a <tt>为<tt>
* null < tt>,在这种情况下,返回<tt> 0 <tt>。 @param一个数组,
* 其数组基于内容的哈希码来计算@返回<tt> a <tt>的基于内容的哈希码
* @see deepHashCode(Object [])@ 1.5起
*
*/
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
通过上述方式重写hashcode,但是这样写也会有冲突,个人认为最好是单个元素计算hashcode,例如将每个变量叠加求md5值以保证唯一性(在不确定实体类中变量在应用的时候唯一)
但是只重写hashcode值这样并不能保证map的在保存的时候能够唯一
将上述实体类中的重写hashcode注释打开,发现hash值相同但 比较的时候并不相同
27392574
27392574
false
{Chengxuyuan{age=15, company='没公司'}=没公司, Chengxuyuan{age=15, company='没公司'}=没公司}
没公司
所以需要重写equals方法
27392574
27392574
false
{Chengxuyuan{age=15, company='没公司'}=没公司, Chengxuyuan{age=15, company='没公司'}=没公司}
没公司
再次运行两个对象就相同了
27392574
27392574
true
{Chengxuyuan{age=15, company='没公司'}=没公司}
没公司
这样就可以插入到map中了 通过实体类的可以就可以获取到元素
来源:https://blog.csdn.net/qq_27081015/article/details/115655081


猜你喜欢
- 本文实例为大家分享了Android实现拍照或者选取本地图片的具体代码,供大家参考,具体内容如下总体流程从selectPhotoActivit
- 本文实例讲述了java数据结构与算法之noDups去除重复项算法。分享给大家供大家参考,具体如下:public static void no
- Java png图片修改像素rgba值import javax.imageio.ImageIO; import javax.swing.Im
- 1.创建列表(列表可以存储任何类型的数据,在创建列表对象的时候首先要指定你要创建的这个列表要存储什么类型的)(泛型)//创建列表  
- 本文实例讲述了Java深度复制功能与用法。分享给大家供大家参考,具体如下:写在前面:什么是深度复制?在Java里面,在创建一个对象,我们通常
- 今天老师想让我帮忙把她们200多张寸照换成白底的,这些寸照里面多为蓝色底,红色底。用ps?不!用java!!对,我第一反应就是用java,到
- 最近ui同学使用了一种新字体(锐字锐线怒放黑简) 发现全部切成图片字体 吓死我了unity3d和fairygui搭配使用字体过程1.第一步肯
- 这个例子用于演示在Spring Boot应用中如何验证Web 应用的输入,我们将会建立一个简单的Spring MVC应用,来读取用户输入并使
- 一、基本概念1、进程首先打开任务管理器,查看当前运行的进程:从任务管理器里面可以看到当前所有正在运行的进程。那么究竟什么是进程呢?进程(Pr
- 本文实例讲述了Android编程判断网络连接是否可用的方法。分享给大家供大家参考,具体如下:为了提高用户体验,我们在开发 android 应
- 1 SharedPreferences 介绍SharedPreferences是使用键值对的方式来存储数据的SharedPreference
- 前几天开发公司项目,有个地方要做一个分数的仪表盘,根据分数跑分,(设计的人估计是看到招商银行App的账号总览)网上好像找不到类似的组件,那就
- 一、分布式锁介绍分布式锁主要用于在分布式环境中保护跨进程、跨主机、跨网络的共享资源实现互斥访问,以达到保证数据的一致性。二、架构介绍&nbs
- 前文传送门:Netty分布式Future与Promise执行回调相关逻辑剖析概述FastThreadLocal我们在剖析堆外内存分配的时候简
- 前言学过定时任务,但是我忘了,忘得一干二净,害怕,一直听别人说:你写一个定时任务就好了。写个定时任务让他去爬取就行了。我不会,所以现在得补回
- 这篇文章主要介绍了Spring Boot Logback配置日志过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考
- 1.先定义一个Java对象Person:public class Person{ String name; int age; int num
- 在Android项目开发过程中,Android Studio是一款非常强大的开发工具。到底有多强大,用了你就知道了。本文我将介绍Studio
- 算法分析一个排序算法的好坏,一般是通过下面几个关键信息来分析的,下面先介绍一下这几个关键信息,然后再将常见的排序算法的这些关键信息统计出来。
- 本文实例为大家分享了java基于UDP实现在线聊天的具体代码,供大家参考,具体内容如下效果图:一、学习UDP的简单使用步骤接收端:Datag