这篇文章将详解介绍 Java 的 equals() 与 hashCode() 方法

我们知道 Object 类是类继承结构的基础,所以是每一个类的父类,所有的对象,包括数组,都实现了在 Object 类中定义的方法。而在 Object 类中有两个非常重要的方法:

public boolean equals(Object obj)
public int hashCode()

这两个方法是息息相关、紧密相连的,下面我们就来详细介绍一下这两个方法的作用。

equals() 方法


equals() 方法是用来判断其他的对象和该对象是否相等。在 Object 类中 equals() 方法的定义如下:

public boolean equals(Object obj) {  return (this == obj);
}

很明显这是对两个对象的地址值进行的比较(即比较引用是否相同),但是我们知道,String 、Math、Integer、Double 等这些封装类在使用 equals() 方法时,已经覆盖了 object 类的 equals() 方法,比如 String 类中的 equals() 如下:

// String 的 equals 方法
public boolean equals(Object anObject) {if (this == anObject) {return true;}if (anObject instanceof String) {String aString = (String)anObject;if (!COMPACT_STRINGS || this.coder == aString.coder) {// 调用 StringLatin1.equals() 方法return StringLatin1.equals(value, aString.value);}}return false;
}// StringLatin1.equals() 方法
public static boolean equals(byte[] value, byte[] other) {if (value.length == other.length) {for (int i = 0; i < value.length; i++) {if (value[i] != other[i]) {return false;}}return true;}return false;
}

很明显,这是进行的内容比较,而已经不再是简单的地址比较了。类似的 Math、Integer、Double 等这些类都是重写了 equals() 方法的,从而进行的是内容的比较,当然,基本类型是进行值的比较。

需要注意的是当 equals() 方法被 override 时,hashCode() 也要被 override,按照一般 hashCode() 方法的实现来说,相等的对象,它们的 hash code 一定相等。

hashcode() 方法


hashCode() 方法给对象返回一个 hash code 值,这个方法用于哈希表(hash tables),例如 HashMap。

hashCode() 需要遵循一下规则:

  • 在一个 Java 应用的执行期间,如果一个对象提供给 equals 做比较的信息没有被修改的话,该对象多次调用 hashCode() 方法时,必须返回同一个值。
  • 如果两个对象根据 equals(Object) 方法是相等的,那么调用二者各自的 hashCode() 方法产生的值必须相等
  • 不相等的两个对象,其 hashCode() 得到的值可以相等也可以不相等,但程序设计时应该尽量让不同对象产生不同的值,这可以提高哈希表的性能

大量的实践表明,由 Object 类定义的 hashCode() 方法对于不同的对象返回不同的值,在 Object 类中,hashCode() 定义如下:

public native int hashCode();

这是一个本地(native)方法,它的实现是和本地机器相关的。当然我们也可以覆盖 hashcode() 方法,比如 String、Integer、Double 等这些类都是覆盖了 hashcode() 方法的。例如在 String 类中定义的 hashcode() 方法如下:

// String 的 hashCode() 方法定义
public int hashCode() {  int h = hash;if (h == 0 && !hashIsZero) {h = isLatin1() ? StringLatin1.hashCode(value): StringUTF16.hashCode(value);if (h == 0) {hashIsZero = true;} else {hash = h;}}return h;
}// StringLatin1.hashCode() 方法定义
public static int hashCode(byte[] value) {int h = 0;for (byte v : value) {h = 31 * h + (v & 0xff);}return h;
}

想要弄明白 hashCode 的作用,必须要先知道 Java 中的集合:

Java 中的集合(Collection)有两类:一类是 List,再有一类是 Set。前者集合内的元素是有序的,元素可以重复,后者元素无序,但元素不可重复。

这里就引出一个问题:要想保证元素不重复,可两个元素是否重复应该依据什么来判断呢?

判断依据就是 Object.equals() 方法。但是,如果每增加一个元素就检查一次,那么当元素很多时,后添加到集合中的元素比较的次数就非常多了。也就是说,如果集合中现在已经有1000个元素,那么第1001个元素加入集合时,它就要调用1000次 equals 方法,这显然会大大降低效率。

于是,Java 采用了哈希表的原理,哈希(Hash)实际上是个人名,由于他提出了哈希算法的概念,所以就以他的名字命名。哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上,可以简单理解为:hashCode 方法实际上返回的就是对象存储的物理地址(实际可能并不是)。

这样一来,当集合要添加新的元素时,先调用这个元素的 hashCode 方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了。如果这个位置上已经有元素了,就调用它的 equals 方法与新元素进行比较,相同的话就不用保存了,不相同的就散列在其它的地址,所以这里存在一个冲突解决的问题。但这样一来,实际调用 equals 方法的次数就大大降低了,几乎只需要一两次就可以了。

总的来说就是:hashcode 能大大降低对象比较次数,提高查找效率。

equals() 和 hashcode()


Java 对象的 eqauls 方法和 hashCode 方法有以下两点规定:

相等或相同的对象必须具有相等的哈希码

想象一下,假如两个 Java 对象 A 和 B 相等(eqauls 结果为 true),但 A 和 B 的哈希码不同,则 A 和 B 存入 HashMap 时的哈希码计算得到的 HashMap 内部数组位置索引就可能不同,那么 A 和 B 很有可能允许同时存入 HashMap,显然相等或相同的元素是不允许同时存入 HashMap 的,因为 HashMap 不允许存放重复元素。

如果两个对象的 hashCode 相同,它们并不一定相同

也就是说,不同对象的 hashCode 可能相同。我们举个例子,假如两个 Java 对象 A 和 B 不相等(eqauls 结果为 false),但 A 和 B 的哈希码相等,当将 A 和 B 都存入 HashMap 时就会发生哈希冲突,也就是 A 和 B 存放在 HashMap 内部数组的位置索引相同,这时 HashMap 会在该位置建立一个链接表,将 A 和 B 串起来放在该位置,显然,该情况不违反 HashMap 的使用原则,是允许的(当然,哈希冲突越少越好,尽量采用好的哈希算法以避免哈希冲突)。

所以,Java 对于 eqauls 方法和 hashCode 方法有这样的规定:

  • 如果两个对象相同,那么它们的 hashCode 值一定要相同
  • 如果两个对象的 hashCode 相同,它们并不一定相同(这里说的对象相同指的是用 eqauls 方法比较)

总结就是:equals() 相等的两个对象,hashcode()一定相等;equals() 不相等的两个对象,hashcode() 可能相等,也可能不等。反过来,hashcode() 不相同,equals() 一定也不等,但 hashcode() 相等,equals() 可能相等,也可能不等

哈希集合与 hashcode()、equals() 的关系


与哈希集合有关的集合有:Hashset、Hashmap、Hashtable等等,它们的存储操作是根据什么原理来存取对象的呢?

下面以 HashSet 为例进行分析,我们都知道:在 hashset 中不允许出现重复对象,元素的位置也是不确定的,那 hashset 中又是怎样判定元素是否重复的呢?

在 java 的集合中,判断两个对象是否相等的规则是:

判断两个对象的 hashCode 是否相等,如果不相等,则认为两个对象也不相等,结束判断。如果 hashCode 不相同,那么就用 equals 判断两个对象是否相等,如果不相等,则认为两个对象也不相等,如果相等则认为两个对象相等(equals()是判断两个对象是否相等的关键

举个例子:

public class HashSetTest {public static void main(String args[]) {String s1 = new String("hello");String s2 = new String("hello");System.out.println("s1 == s2 : " + s1 == s2);System.out.println("s1.equals(s2) : " + s1.equals(s2));System.out.println("s1.hashCode() : " + s1.hashCode());System.out.println("s2.hashCode() : " + s2.hashCode());Set hashset = new HashSet();hashset.add(s1);hashset.add(s2);Iterator it = hashset.iterator();while (it.hasNext()) {System.out.println(it.next());}}
}>>>>>
s1 == s2 : false
s1.equals(s2) : true
s1.hashCode() : 87652
s2.hashCode() : 87652
hello

这个例子中,由于 String 类已经重写了 equals() 和 hashcode() 方法(比较的是字符串内容),所以 hashset 认为它们是相等的对象,而没有重复添加进去,所以迭代的时候只输出一个字符串内容。

所以我们可以总结出:如果自己定义的类中如果没有重写 equals() 和 hashcode() 方法,那么添加对象到集合里时就会调用父类 Object 的 equals() 和 hashcode() 方法。而 Object 类中的 hashcode() 方法是一个本地方法,比较的是对象的地址(引用地址),所以使用 new 方法创建的对象当然是不同的对象了,造成的结果就是两个对象的 hashcode() 返回的值不一样,这样会导致哈希集合会把它们当作不同的对象对待。

为了避免这种情况,我们建议:自定义类时应该重写 equals() 和 hashcode() 方法

【Java基础】之深入讨论equals()和hashcode()方法相关推荐

  1. Java基础提升篇:equals()与hashCode()方法详解

    概述 java.lang.Object类中有两个非常重要的方法: public boolean equals(Object obj) public int hashCode() Object类是类继承 ...

  2. 【JAVA基础篇】==、equals和hashCode的区别和联系

    == 作用:比较两个操作数的关系,返回一个boolean类型的结果 具体含义:如果两个操作数是基本数据类型,比较值是否相等.如果两个操作数是引用类型,那么比较的是内存地址是否相同. equals Ob ...

  3. Java拾遗:001 - 重写 equals 和 hashCode 方法

    2019独角兽企业重金招聘Python工程师标准>>> 重写equals方法 在Java中Object类是一个具体类,但它设计的主要目的是为了扩展,所以它的所有非final方法,都被 ...

  4. 搞懂 Java equals 和 hashCode 方法

    搞懂 Java equals 和 hashCode 方法 分析完 Java List 容器的源码后,本来想直接进入 Set 和 Map 容器的源码分析,但是对于这两种容器,内部存储元素的方式的都是以键 ...

  5. Java基础-Integer的==和equals方法

    Java基础-Integer的==和equals方法 1.首先说下 equals 方法: ​ equals 方法接受的参数为 Object 类型 equals(Object obj),首先会判断参数中 ...

  6. java baseentity_如何在JPA的BaseEntity中实现equals()和hashcode()方法?

    我有一个BaseEntity类,它是我的应用程序中所有JPA实体的超类. @MappedSuperclass public abstract class BaseEntity implements S ...

  7. List去重为什么要写equals(),hashCode()方法

    一,各个集合的特点: Collection(集合):容器,用于存放对象(引用类型.基本类型需要自动装箱) List(列表):元素有序,元素可以重复 (有索引). 通过元素的equals()方法判断是否 ...

  8. 深入理解Java的equals和hashCode方法

    1.谈谈equals方法 相信大家对这个这个方法一定不陌生.该方法是Object基类里的非final方法(被设计成可覆盖的),下面我们来看看Object中是如何实现该方法的.源代码如下: public ...

  9. 学习Java编程equals()和hashCode()方法

    equals()和hashCode()区别? equals():反映的是对象或变量具体的值,即两个对象里面包含的值--可能是对象的引用,也可能是值类型的值. hashCode():计算出对象实例的哈希 ...

最新文章

  1. spring boot validated的使用
  2. Reverse Linked List
  3. 算算是第几天的C语言程序,计算当日是该年的第几天---C语言
  4. github issue 搜索_回顾 2020 年 GitHub 的大事件,你知道多少?
  5. 阿里云服务器ECS和腾讯云服务器如何安装宝塔面板?
  6. 关于安装CNPM 与搭建VUE空白项目
  7. es6异步编程 Promise 讲解 --------各个优点缺点总结
  8. vue项目运用繁体字
  9. python 根据x的值和函数y=20+x2,计算y_new,算出y_new和y的差,记为delta_y。¶绘制x和delt_y的点图,并计算y的方差。有关方差的计算参阅数学资料。
  10. 微信文件下载内容如何调整存储位置?
  11. linux查看hive账户权限,Linux用户和权限管理
  12. 后台管理系统模板简介
  13. 域格EVB开发板使用说明
  14. 正大国际期货:做期货主帐户有什么风险?
  15. 视频画面滚动字幕怎么做,让你几分钟学会的方法
  16. html中鱼眼效果,鱼眼效果和放大效果怎么做
  17. 喜闻乐见之Activity生命周期
  18. jenkins远程执行脚本不退出Exec in pty
  19. CAD转JPG,在线转换,没有转换器
  20. ABB AC500 系列 PLC 与上位机iFix 的通讯配置

热门文章

  1. 【Java】iText POI
  2. oracle中ccuser,Oracle数据库查询与SESSIONS_PER_USER大于40
  3. linux显示器镜像翻转,Ubuntu Linux显示器屏幕偏移的解决办法
  4. boost:chrono
  5. 装ghost遇到的问题之一“Windows failed to start. A Recent hardware or software change might be the cause.”
  6. Word文件删除后怎么恢复?好用的恢复方法分享
  7. STL算法——常用查找算法(find、find_if、adjacent_find、binary_search、count、count_if)
  8. Path环境变量的配置
  9. TextView的android:maxHeight,android:minHeight的正确设置
  10. 关于微信小程序form表单无法触发提交的解决方案