胡荣良

胡荣良

工程架构 | 算法

© 2019

Java中的hashCode()和equals()

这两个方法比较常见,深究下会发现很有意思。

什么是hashCode?

他是根据对象的某些特性计算出的一个整数值。

hashCode有什么用?

一般没什么用,只有在查找时有用。比如对象放在哈希表里,存放位置是根据hascode计算出来的。这时候要查找某个对象,只要用这个对象的hashcode计算一下就能直接定位。

怎么得到一个对象的hashCode?

调用这个对象的hashCode方法。

hashCode方法定义在跟对象Object上,所以每个对象都有hashCode方法。

Object对象的hashCode是怎么算出来的?如何自己设计hashCode算法?

Object类的hashCode实现方法是:将对象的内存地址转换为整数作为hashCode。

java.lang.Object中的hashCode()方法对hashCode做了如下规范:

  • 在对象的生命周期内,每次调用hashCode方法返回的值必须一样。
  • 不同的对象生命周期,其hashCode可以不一样。比如跑在服务器jvm里的一个固定日期的Date对象,他的hashCode和重启服务器后的hashCode可以不一样。
  • 如果两个对象通过equals方法比较结果相同,则他们的hashCode方法放回的值必须是一样的。
  • 如果两个对象通过equals方法比较结果不同,择他们的hashCode方法可以一样也可以不一样,没有硬性规定。但是通常建议不一样,目的是为hash table提供更好的查找性能。

一个较合理的设计是不同对象的hashCode应该是不同的。

JDK中有哪些常用的类的hashCode被重写了?

  • java.lang.Integer hashCode等于他的基本类型值。
  • java.lang.String 计算公式:s[0]31^(n-1) + s[1]31^(n-2) + … + s[n-1]。所以只要代表的字符串值一样,hashCode就是一样的。

equals方法有什么用?

比较两个对象是否相同

Object的equals方法怎么实现的?自己如何设计equals方法?

Object的equals方法的实现是直接比较两个对象的引用地址(a==b)

设计equals方法需遵循如下原则:

  • 自反性:x.equals(x)应该为true
  • 对称性:如果a.equals(b)为true,b.equals(a)==true
  • 传递性:如果a.equals(b)为true,b.equals(c)为true,则a.equals(c)为true
  • 一致性:如果a、b没有的内容没有改变,则多次调用a.equals(b)返回的结果应该相同
  • a.equals(null)应该返回false

什么样的情况需要重写hashCode和equals方法?有哪些要主意的?

需要根据对象的内容来确定两个对象是否相同时需要重写hashCode和equals方法。

重写hashCode往往是为了配合重写equals,因为hashCode规范里规定,如果equals方法返回true,则两个对象的hashCode值必须一直。

判断两个对象需要同时调用他们的hashCode方法和equals方法吗?

一般只要调用equals方法就行了。

如果要提高比较效率,可以先比较两个对象的hashCode值,如果相同再调用equals方法判断。这也是JDK里经常用的策略。这也是为什么要同时重写hashCode和equals方法的原因,只有遵守hashCode规范才能与JDK或这其他类库达成一致。

理解hashtable查询对象的内部逻辑有助于更好的理解hashCode和equals方法。

hashtable里维护着一个table数组,table数组里的每个元素是一个Entry链表,hashtable的对象实际存储在Entry链表里。

存储规则:根据对象的hashCode找到table数组里对应地Entry链表(根据hashCode算出table数组里这个Entry元素的下标),将对象添加在Entry链表尾部。

判断是否存在的规则:根据对象的hashCode先定位到table数组里对应地EntryEntry链表,逐个遍历Entry链表,调用数据的equals方法比较链表元素,找到比较结果为true的对象即为要找到对象。