Java是很多开发者都曾经接触过的一门开发语言,它之所以流行,那是因为他有很多的设计思想解决了很多现实中的问题,其中对象的equals方法,hashCode方法的设计思想值得我们学习,所以我们有必要去深入学习一下这两个方法。

1:hashCode的作用

hashcode的作用大概归纳为如下几点:

  1. hashCode的存在主要用于查找的快捷性,如HashtableHashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的。

  2. 如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同。

  3. 如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点。

  4. 两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。

1.hashcode是用来查找的,如果你学过数据结构就应该知道,在查找和排序这一章有
例如内存中有这样的位置
0  1  2  3  4  5  6  7
而我有个类,这个类有个字段叫ID,我要把这个类存放在以上8个位置之一,
如果不用hashcode而任意存放,那么当查找时就需要到这八个位置里挨个去找,或者用二分法一类的算法。
但如果用hashcode那就会使效率提高很多。
我们这个类中有个字段叫ID,那么我们就定义我们的hashcode为ID%8,
然后把我们的类存放在取得得余数那个位置。比如我们的ID为9,
9除8的余数为1,那么我们就把该类存在1这个位置,
如果ID是13,求得的余数是5,那么我们就把该类放在5这个位置。
这样,以后在查找该类时就可以通过ID除 8求余数直接找到存放的位置了。  2.但是如果两个类有相同的hashcode怎么办那(我们假设上面的类的ID不是唯一的),
例如9除以8和17除以8的余数都是1,那么这是不是合法的,
回答是:可以这样。那么如何判断呢?在这个时候就需要定义 equals了。
也就是说,我们先通过 hashcode来判断两个类是否存放某个桶里,
但这个桶里可能有很多类,那么我们就需要再通过 equals 来在这个桶里找到我们要的类。
那么。重写了equals(),为什么还要重写hashCode()呢?
想想,你要在一个桶里找东西,你必须先要找到这个桶啊,
你不通过重写hashcode()来找到桶,光重写equals()有什么用啊

上述回答转载于:Java中hashCode的作用 。由于作者总结的太好,所以直接转载了,希望对你有所帮助。

看看Object对象实例hashCode代码如下:

package java.lang;
public class Object {·······/*** 返回该对象的哈希码值。* 支持此方法是为了提高哈希表(例如 java.util.Hashtable 提供的哈希表)的性能* {@link java.util.HashMap}.* <p>* hashCode 的常规协定是:* <ul>* <li>在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,*     必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改。*     从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。* <li>如果根据 equals(Object) 方法,两个对象是相等的,*     那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果。* <li>如果根据 equals(java.lang.Object) 方法,两个对象不相等,*     那么对这两个对象中的任一对象上调用 hashCode 方法不 要求一定生成不同的整数结果。*     但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。* </ul>* <p>* 实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。* (这一般是通过将该对象的内部地址转换成一个整数来实现的,* 但是 JavaTM 编程语言不需要这种实现技巧。)** @return## 标题  此对象的一个哈希码值。* @see     java.lang.Object#equals(java.lang.Object)* @see     java.lang.System#identityHashCode*/public native int hashCode();
·······
}

2:如何理解equals(Object obj)方法,它和 == 操作符相比,有什么区别?

  1. == 操作符分为两种情况

    • 比较基础类型(byte,short,int,long,float,double,char,boolean)时,比较的是值是否相等
    • 比较对象,比较的是对象在内存中的空间地址是否相等。
  2. equals(Object obj)方法比较也分为两种情况:

    • 如果一个类没有重写equals(Object obj)方法,则等价于通过==比较两个对象,即比较的是对象在内存中的空间地址是否相等。
    • 如果重写了equals(Object obj)方法,则根据重写的方法是内容去比较是否相等,返回true则相等,false则不相等。

3: 如果您去重写equals(Object obj)方法,您会怎么做?重写的过程又需要注意什么?

我会遵守JAVA官方的通用约定,根据官方实例代码,简述约定流程如下:

  1. 自反性:对于非 null 的对象 obj,必须有 obj.equals(obj)=true

  2. 对称性:如果 objx.equals(objy)=true,那么 objy.equals(objx) 必须也为true

  3. 传递性:如果 objx.equals(objy)=true 而且 objy.equals(objz)=true,那么objx.equals(objz) 必须为true

  4. 对于非 null 的对象 obj,一定有obj.equals(null)=false

  5. equals(Object obj)方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。

根据以上约定我们就可以按照如下步骤去重写equals方法。

重写equals方法步骤如下:

  1. 先使用 == 操作符判断两个对象的引用地址是否相同。
  2. 使用instanceof来判断 两个对象的类型是否一致。
  3. 如果类型相同,则把待比较参数转型,逐一比较两个对象内部的值是否一致,全部一致才返回true,否则返回false
  4. 重写hashCode方法,确保相等的两个对象必须具有相等的哈希码

注意:

我们在重写一个类的hashCode方法时,最好是将所有用于相等性检查的字段都进行hashCode计算,最后将所有hashCode值相加,得出最终的hashCode,这样可以保证hashCode生成均匀,不容易产生碰撞。

常见数据类型hashcode计算方式如下:

重要字段var的类型 hash运算
byte,short,int,char (int)var
long (int)(var ^ (var >>> 32))
float Float.floatToIntBits(var)
double long bits = Double.doubleToLongBits(var);分量 = (int)(bits ^ (bits >>> 32))
引用类型 (null == var ? 0 : var.hashCode())


示例代码:

/*** 指示其他某个对象是否与此对象“相等”。* <p>* equals 方法在非空对象引用上实现相等关系:* <ul>* <li>自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true。** <li>对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,* x.equals(y) 才应返回 true。** <li>传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,* 并且 y.equals(z) 返回 true,那么 x.equals(z) 应返回 true。** <li>一致性:对于任何非空引用值 x 和 y,多次调用 x.equals(y) 始终返回 *  true 或始终返回 false,前提是对象上 equals 比较中所用的信息没有被修改。* * <li>对于任何非空引用值 x,x.equals(null) 都应返回 false。* </ul>* * <p>* Object 类的 equals 方法实现对象上差别可能性最大的相等关系;* 即,对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,* 此方法才返回 true(x == y 具有值 true)。* * <p>* 注意:当此方法被重写时,通常有必要重写 hashCode 方法,* 以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。** @param   要与之比较的引用对象。* @return  如果此对象与 obj 参数相同,则返回 true;否则返回 false。* @see     #hashCode()* @see     java.util.HashMap*/public boolean equals(Object obj) {return (this == obj);}

4.如果要您去维护一个类的hash散列表,您会如何设计,如何解决hash冲突?

我们在设计类的hash散列表时,不能保证每个元素的hash值都是不一样的,这样就会造成hash冲突。解决hash冲突有如下4种方法:

  1. 开发定址法:既然当前位置容不下冲突的元素了,那就再找一个空的位置存储 Hash 冲突的值(当前 index 冲突了,那么将冲突的元素放在 index+1)。

  2. 再散列法:换一个 Hash 算法再计算一个 hash 值,如果不冲突了就存储值(例如第一个算法是名字的首字母的 Hash 值,如果冲突了,计算名字的第二个字母的 Hash 值,如果冲突解决了则将值放入数组中)。

  3. 链地址法:每个数组中都存有一个单链表,发生 Hash 冲突时,只是将冲突的 value 当作新节点插入到链表(HashMap 解决冲突的办法)。

  4. 公共溢出区法:将冲突的 value 都存到另外一个顺序表中,查找时如果当前表没有对应值,则去溢出区进行顺序查找。

5.总结

  1. 当你真要的需要重写equals方法,这两点一定要记住:

    • A.如果两个对象相等(equals() 返回 true),那么它们的 hashCode()一定要相同;

    • B.如果两个对象hashCode()相等,它们并不一定相等(equals() 不一定返回 true)。

  2. 如果重写的equals方法但不重写hashCode,都是耍流氓,会有意想不到的结果。

  3. 重写hashCode方法时,尽可能将所有用于相等比较的参数都参与hashCode的计算。

  4. 建立hash散列表的意义就是在于,提高查询效率,当数据量大时,尤为显著。

参考资料:

  • Java中hashCode的作用
  • 如何正确实现 Java 中的 HashCode
  • 程序员必须搞清的概念equals和=和hashcode的区别
  • Android 面试准备之「equals 和 == 」

Java基础之深入认识hashCode和equals相关推荐

  1. Java基础系列:重写hashCode和equals

    1 场景 Map的key设置为对象时,必须重写对象的hashCode和equals方法. 原因 通过对象取值时,及时相同的对象(初始化值相同),get时,会出现null值. 方案 重写对象的hashC ...

  2. java中Object类的hashCode和equals及toString方法。

    java中的hashcode.equals和toString方法都是基类Object的方法. 首先说说toString方法,简单的总结了下API说明就是:返回该对象的字符串表示,信息应该是简明但易于读 ...

  3. Java基础学习总结(1)——equals方法

    2019独角兽企业重金招聘Python工程师标准>>> 一.equals方法介绍 1.1.通过下面的例子掌握equals的用法 1 package cn.galc.test; 2 3 ...

  4. Java基础Object类的hashCode方法

    hashCode方法: 在Object中的hashCode方法是怎样的? public native int hashCode(); 这个方法不是抽象方法,带有native关键字,底层调用C++程序. ...

  5. Java基础361问第5问——equals和==的区别

    判断两个数或者两个对象是否相等,我们一般使用equals或者==比较,但是为什么String字符串的比较一般使用equals而基本数据类型的比较使用= =呢? 先说结论 1 equals是方法,==是 ...

  6. 【重难点】【Java基础 03】hashCode() 和 equals()、代理模式

    [重难点][Java基础 03]重写hashCode() 和equals(). 文章目录 [重难点][Java基础 03]重写hashCode() 和equals(). 一.hashCode() 和 ...

  7. Java实习生常规技术面试题每日十题Java基础(二)

    目录 1. JAVA 的反射机制的原理. 2.静态嵌套类(Static Nested Class)和内部类(Inner Class)的不同? 3.如何将String类型转化成Number类型. 4.什 ...

  8. java 调用关系_【Java基础】几种简单的调用关系与方法

    直接上代码吧. class lesson4AB //同一个类下的public修饰的方法A,B可以相互调用 { public void A() { B();//等价于this.B(); } public ...

  9. Java面试之Java基础下册(含答案)

    动态代理与cglib实现的区别. 动态代理有两种实现方式,分别是:jdk动态代理和cglib动态代理 jdk动态代理的前提是目标类必须实现一个接口,代理对象跟目标类实现一个接口,从而避过虚拟机的校验. ...

最新文章

  1. SpringBoot 整合 Shiro 实现动态权限加载更新+ Session 共享 + 单点登录
  2. 菜鸟、普通、老鸟程序猿如何写奇数判断?--位操作符妙用
  3. 最好用的koa2+mysql的RESTful API脚手架,mvc架构,支持node调试,pm2部署。
  4. 几种服务器端IO模型的简单介绍及实现(转载)
  5. UNIX标准化及实现之POSIX标准可选头文件
  6. Vue3.x 使用ref和reactive、toRef
  7. java 项目拆分_java – 多模块项目什么时候应该拆分成单独的存储库树?
  8. 关于C#中timer类
  9. Struts2 stracture
  10. LeetCode 226 翻转二叉树
  11. 小试牛刀——搭建一个周报管理系统
  12. python爬虫豆瓣推理书籍及链接
  13. python和R的区别
  14. matlab---之imcrop
  15. 营救公主的100种方法
  16. 【论文解读】(2019-EMNLP)Tackling Long-Tailed Relations and Uncommon Entities in Knowledge Graph Completi
  17. 【7 kyu】Descending Order
  18. win7设置计算机共享的打印机共享的打印机共享,告诉你win7打印机共享设置教程...
  19. Java程序设计 试卷A
  20. 电脑能正常上网百度,但是网络显示无Internet

热门文章

  1. 打开QQ快捷键截屏 CTRL+ALT+A
  2. 中药和西药的历史渊源,到底谁才是科学好药
  3. 一起来玩AZURE SQL(二)AZURE SQL 初级使用篇
  4. 百度飞桨 如何撑起了AI产业生态?
  5. 微信发虎年新春贺词领福袋:游戏皮肤、QQ音乐VIP、现金红包等
  6. FF:纳斯达克要求退市系误读 警示函仅与推迟提交Q3财报相关
  7. 飞书正式发布5.0版 推出飞书人事、合同、审批等多款新产品
  8. 华为Mate50系列今年没戏:或明年第二季度末发布 5G有望回归
  9. 特斯拉:召回不涉及国产车型 也与“刹车失灵”无关
  10. COACH与得物App达成官方合作 未来计划提供专供款商品