Java 的 hashCode 与 equals

Java 的 hashCode 和 equals 是 java.lang.Object 的两个方法,这意味着所有的 Java 对象都存在这两个方法。当我们在使用 Map、Set 数据结构时,都要或多或少的和此方法打交道。亦或使用 Hibernate 在定义 Entity 实体类时,也需要注意这两个方法的重写。掌握 hashCode 与 equals 方法有利于加深对 Java 数据结构的理解,并且还能帮助我们避免一些低级 Bug。

Kotlin 与 Java 类似,但 Kotlin 的一些语法糖使得背后的类转换变得隐晦起来。例如 Kotlin 的比较操作符 == 实际调用的是类的 equals;Kotlin 可以针对列表进行取差操作,而这个操作的背后需要先把对应数据集合类型转成 HashSet ,然后使用 HashSet 的取差方法。

hashCode

hashCode 方法是由 Java 的哈希算法生成的 int 类型哈希值,既然时哈希值,这就意味着两个不同的对象也可能拥有相同的哈希值

哈希算法是把任意长度的输入转换成定长输出。

在 JavaDoc 中有说明此方法主要用于需要哈希表结构的数据结构,如 java.util.HashMapjava.util.HashSet ,hashCode 方法受限于以下三个法则:

  • 在程序运行过程中,hashCode 结果是不变的。
  • 如果两个对象 equals 相等,则 hashCode 方法必须相等。
  • 不要求两个 hashCode 相等的对象,equals 也相等。

这三个法则浓缩成一句话:

在程序运行阶段时,两个 equals 相等的对象,hashCode 结果值必须相等。

为什么这么要求呢?因为在使用哈希结构查询数据时,如使用 containsgetremove 等操作时,都会先使用哈希值匹配对应的 bucket,当多个对象出现哈希冲突时,在一个 bucket 中会存在以链表方式连结的对象列表,然后逐个使用 equals 方法进行匹配,以提高查询效率。

任何对象都有 hashCode 方法,如果没有手动重写,Object 的原生实现则会在某种程度上使用内存地址。

不同的 JVM,对 hashCode 的具体实现是不一样的。

equals

equals 方法相对而言就单纯了一些,这个方法就是用来比较两个对象的逻辑相等。类是编程思想中用于对现实世界建模抽象的工具,对应现实生活中的一类事物,而对象则是对应现实生活中的一个实体。现实生活中的实体都是可区分的,具有可标识性;在面向对象编程中使用对象映射现实生活中的实体,要保证对象的标识性,则是使用 equals 方法进行比对。

所以当我们要重写 equals 方法时,需要遵循的原则就是要让对象具有可区分性,能够和现实实体相对应。

开发中需要注意什么

平时开发过程中,大多数类都会使用内置的 hashCode 和 equals 方法,这对日常的开发过程没有任何影响,这常常会让我们忽略了它的重要性,这会在某些情况下造成难以察觉的Bug。

所以需要加深对此的理解,尤其是使用 ORM 或集合操作时,一定要注意 hashCode 和 equals 方法的重写。只要遵循上面提到的法则,就能够避免很多问题了。

Also See

作者:风逝

兴趣爱好广泛,哲学 & 心理学 & 经济学 & 编程技术等方面,经常会分享读书笔记和想法,希望能够认识志同道合的朋友。你有任何看法或建议,都可以直接在文章中评论。

发表评论

您的电子邮箱地址不会被公开。