加快判断两个对象是否相等
为什么是“加快”判断呢?接下来我们好好履履什么是hashCode,还有hashCode的作用场景,只要清晰的认识到hashCode的整个逻辑,什么八股“hashCode跟equals”这种问题再也不会难倒了。
1、hashCode是什么?
1.1、从源码看起
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <p>
* The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code equals} comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
* <li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link java.lang.Object#equals(java.lang.Object)}
* method, then calling the {@code hashCode} method on each of the
* two objects must produce distinct integer results. However, the
* programmer should be aware that producing distinct integer results
* for unequal objects may improve the performance of hash tables.
* </ul>
* <p>
* As much as is reasonably practical, the hashCode method defined by
* class {@code Object} does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java™ programming language.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
public native int hashCode();
我简单的翻译一下,主要有这么几个点:
- 返回一个对象的hash码,可以为hash表这样的hash数据结构提供便利
- 在一个Java进程内,一个对象的hashCode 始终是相等的
- 硬性要求,如果通过equals比较两个对象是否相等时,那么一定要保证相等的两个对象的hashCode也是相等的
- 它不是必需的,如果按照equals(java.lang.Object)法两个对象是不平等的,然后调用hashCode方法每一个对象必须产生不同的整数结果。然而,程序员应该意识到,产生不同的整数结果的不平等的对象可能会提高哈希表的性能。
- 这个hashCode是由内存地址计算得到的。
我们可以得到结论就是,判断两个对象是否相等在Java中根本跟hashCode没半毛线关系,用的是equals方法判断两个对象是否相等。但是有些对象equals会消耗比较多的性能,比如String对象需要对每个字符做比较,所以利用了hashCode先做一次预判断(O(1)),当hashCode相等的时候再使用equals再做判断。关于这个点,可以从我们熟悉的HashMap里面得到答案。每次判断key相等时候都会想比较hash(这个值是从hashCode计算出来的),然后再==,然后再equals。这个过程已经可以筛选掉一大波不击中的key了。
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
为什么hashCode为什么就能确定两个对象不一样?这个方法是native的,在网上找了一张图,我们通过这张图,来了解到它的计算方式
1.2、hashCode的生成机制
参考:https://blog.caoxudong.info/blog/2013/12/12/default_hashcode_implmentation_in_hotspot
得到的结论就是:hashCode会被记录到对象头里面,生成方式是根据对象地址来做移位跟与操作得到的。所以就有了下面的问题,也是之前的一篇文章提到过的。
1.3、hashCode与==的关系
==:这个符号是判断两个对象的内存地址是否一致
hashCode:是通过内存地址计算出来的
所以,我们在不覆盖hashCode方法的情况下,可以姑且的认为等于一个内存地址的一个标志,但是毕竟是hash算法,肯定会存在hash冲突的情况,这里就不深究下去了。所以Java明确告诉你判断两个对象是否相等用的是equals方法,除了你自己,JVM也不知道这两个对象是否相等。
2、为什么要使用hashCode
看完了上述的描述之后,我们就可以得到答案了,就是回归点题的问题,它存在的必要性就是加速两个对象的判断过程。
还有其二,其二就是方便于hash表的使用,hash表可以根据这个hashCode去计算一个实用的hash码做hash处理。
3、如何正确的使用hashCode
正确使用hashCode码的方式无非就是老生长谈的equals跟hashCode的问题。那就是如果重写了equals的时候,一定要重写hashCode方法。一般来说可以自己根据业务逻辑设计一个hash算法,算出两个对象是否相等,而不仅仅使用内存地址。特别要注意的是在hashMap中去使用这个的时候。