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.HashMap
、java.util.HashSet
,hashCode 方法受限于以下三个法则:
- 在程序运行过程中,hashCode 结果是不变的。
- 如果两个对象 equals 相等,则 hashCode 方法必须相等。
- 不要求两个 hashCode 相等的对象,equals 也相等。
这三个法则浓缩成一句话:
在程序运行阶段时,两个 equals 相等的对象,hashCode 结果值必须相等。
为什么这么要求呢?因为在使用哈希结构查询数据时,如使用 contains
、get
、remove
等操作时,都会先使用哈希值匹配对应的 bucket,当多个对象出现哈希冲突时,在一个 bucket 中会存在以链表方式连结的对象列表,然后逐个使用 equals 方法进行匹配,以提高查询效率。
任何对象都有 hashCode 方法,如果没有手动重写,Object 的原生实现则会在某种程度上使用内存地址。
不同的 JVM,对 hashCode 的具体实现是不一样的。
equals
equals 方法相对而言就单纯了一些,这个方法就是用来比较两个对象的逻辑相等。类是编程思想中用于对现实世界建模抽象的工具,对应现实生活中的一类事物,而对象则是对应现实生活中的一个实体。现实生活中的实体都是可区分的,具有可标识性;在面向对象编程中使用对象映射现实生活中的实体,要保证对象的标识性,则是使用 equals 方法进行比对。
所以当我们要重写 equals 方法时,需要遵循的原则就是要让对象具有可区分性,能够和现实实体相对应。
开发中需要注意什么
平时开发过程中,大多数类都会使用内置的 hashCode 和 equals 方法,这对日常的开发过程没有任何影响,这常常会让我们忽略了它的重要性,这会在某些情况下造成难以察觉的Bug。
所以需要加深对此的理解,尤其是使用 ORM 或集合操作时,一定要注意 hashCode 和 equals 方法的重写。只要遵循上面提到的法则,就能够避免很多问题了。
发表回复