一文告诉你为什么要重写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
猜你喜欢
- 导语相信大家无论是做前端还是做后端的,都被接口接口文档所折磨过,前端抱怨接口文档和后端给的不一致,后端抱怨写接口文档很麻烦,所以Swagge
- 需求: 给定一个URL地址, 例如: http://www.cncounter.com/tools/shorturl.php, 解析对应的I
- 0.前言HashMap简述:HashMap 基于哈希表的 Map 接口实现,是以 key-value 存储形式存在,即主要用来存放键值对。H
- 1、volley 项目地址 https://github.com/smanikandan14/Volley-demo (1)&nb
- GlobalLock的作用对于某条数据进行更新操作,如果全局事务正在进行,当某个本地事务需要更新该数据时,需要使用@GlobalLock确保
- 一、题目描述题目:同步锁出现的目的就是为了解决多线程安全问题。同步锁的几种方式synchronized1、同步代码块2、同步方法jdk1.5
- 参考dubbo和shenyu网关实现自定义的SPISPI标注注解标注提供SPI能力接口的注解@Documented@Retention(Re
- struts2可以非常简单地使用FreeMarker模板作为视图技术,对于传统的jsp页面而言,FreeMarker是一个绝佳的替代方案。除
- 先上代码新建一个Thread,代码如下:package com.thread.test;public class MyThread exte
- 大家对于 Spring 的 scope 应该都不会默认。所谓 scope,字面理解就是“作用域”、“范围”,如果一个 bean 的 scop
- 本文实例为大家分享了java登录界面的具体实现代码,供大家参考,具体内容如下1. Login.javapackage wzb;import
- 使用spring redis的increment方法时,报错:nested exception is redis.clients.jedis
- 前言加密配置是一个很常见的需求,在spring boot生态中,已经有非常多的第三方starter实现了,博主所在公司也有这种强制要求,一些
- 一、问题描述今天使用SDK Manager将Android SDK的版本更新到了Android 5.1的版本,eclipse创建androi
- maven打包指定jdk的版本问题今天遇到个问题,项目中新写了一个接口,其中用到了lambda表达式,本地跑是没问题的,但提交到gitLab
- PS:本文包含了大部分strings函数的说明,并附带举例说明。本来想自己整理一下的,发现已经有前辈整理过了,就转了过来。修改了原文一些源码
- 目录1.堆空间的基本结构:2.空间分配担保机制3.如何判断一个对象已经无效4 不可达的对象并非“非死不可”5 如何判断一个常量是废弃常量?6
- 前言本篇文章 中写到的是 flutter 调用了Android 原生的 TextView 案例添加原生组件的流程基本上可以描述为:1 and
- ReentrantLock内部由Sync类实例实现。Sync类定义于ReentrantLock内部。Sync继承于AbstractQueue
- 今天做统计时需要对X轴的地区按照地区代码(areaCode)进行排序,由于在构建XMLData使用的map来进行数据统计的,所以在统计过程中