一.引言
首先 hashCode() 方法被设计在 Object 类中,说明是希望所有的对象都能实现该方法,该方法是会返回一个对应于当前对象的一个整数,之所以所有对象都实现该方法是为了将来对象在查找时变得快捷。
- Java中的集合类 hashMap、hashSet 底层的实现都是哈希表,之所以采用哈希表,是因为他能克服链表和二叉树等数据结构在查询上的低效率问题,哈希表的存储就是根据对象的哈希值,以此确定他的存储位置。
- 但是,仅仅通过 hashcode() 并不能将所有对象区分开来,有可能会出现对象不同,hashcode 值却相同的情况。
- 为了充分将对象区分开,Object 类中还有一个方法 equals(), 该方法便是在当对象的 hashcode 值相同的情况下,判断两个对象是否相同。
- 通过上面分析可知,hashCode() 方法和 equals() 方法往往都是配合使用的,当我们重写了 equals() 方法时,必须要重写hashCode方法,这是java中的一条规定。
- 原因很简单,假如此处有 A,B 两个对象,他们内容相同,当我们在重写equals() 之前,equals 值是 false,因为重写之前 equals 相当于 ==,对于引用类型来说,就是比较地址值是否相同,但是这是两个对象,地址值肯定不同。而且他们的 hashCode 值也不相同,因为为重写前的 hashCode 值与 对象存储位置 有关,而这是两个对象,所以存储位置肯定不同。因此重写这两个方法前,它们的值都不等。
- 但是,重写之后 equals() 返回了 true,因为这两个对象的内容是相同的,而此时他们的 hashCode 值仍然不相同,因为没有重写这个方法。这明显就不符合逻辑,因为 equals() 相同的对象 hashCode 值肯定相同,因此重写 equals 的同时,有必要重写 hashCode (根据自己的需求来写) 方法,总之两者要同时进行。
- 通过上面的分析,我们发现 hashCode() 方法无非就是用来确定对象的存储位置的,以及对象之间进行比较,这些都是为哈希表的查找效率进行服务的。
二.具体分析
总的来说,Java 中的集合(Collection)有两类,一类是List,再有一类是Set。
前者集合内的元素是有序的,元素可以重复;后者元素无序,但元素不可重复。
问题:要想保证元素不重复,应该怎么来判断呢?
-
这就要用到 Object.equals()方法了。但是,如果每增加一个元素就检查一次,那么当元素很多时,添加到集合中的元素比较的次数就非常多了。 也就是说,如果集合中现在已经有1000个元素了,那么第 1001 个元素加入集合时,它就要调用1000次 equals()方法。这显然会大大降低查询效率。
-
于是,Java采用了哈希表的原理,来解决查找效率低下的问题。
哈希算法也称为散列算法,是将数据依特定算法直接指定到一个地址上。可以这样理解,hashCode() 方法实际上返回的就是对象存储的物理地址。 这样一来,当集合要添加新的元素时,先调用这个元素的 hashCode() 方法,就一下子能定位到它应该放置的物理位置上。 如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有了元素, 就调用它的 equals() 方法与该元素进行比较,相同的话就不存了,不相同的话就散列在其它的地址上。但是这里存在一个冲突解决的问题。一般采用的都是链地址法。
所以,Java 对于 eqauls() 方法和 hashCode() 方法是这样规定的:
1、如果两个对象相同,那么它们的 hashCode 值一定要相同;
2、如果两个对象的 hashCode() 值相同,这两个对象并不一定相同 ;
总结:
1、hashCode() 方法的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode() 方法是用来在散列存储结构中确定对象的存储地址的;
2、如果两个对象相同,就是适用于 equals(Java.lang.Object) 方法,那么这两个对象的 hashCode 一定要相同;
3、如果对象的 equals() 方法被重写,那么对象的 hashCode() 也尽量重写;
4、两个对象的 hashCode 值相同,并不一定表示两个对象就相同,也就是不一定适用于 equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如 Hashtable,他们“存放在同一个篮子里”。
再归纳一下就是 hashCode() 是用于查找使用的,而 equals() 是用于比较两个对象的是否相等的。
例如内存中有这样的位置 :
0 1 2 3 4 5 6 7
而我有个类,这个类有个字段叫 ID , 我要把这个类存放在以上 8 个位置之一,如果不用 hashcode 而任意存放,那么当查找时就需要到这八个位置里挨个去找,判断是否存在相同的元素。但如果用 hashcode 那就会使效率提高很多。
但是如果两个类有相同的 hashcode 怎么办?
------例如 9除以 8和 17除以 8的余数都是1,那么这是不是合法的,回答是:可以这样。那么如何判断呢?在这个时候就需要调用 equals() 了
也就是说,我们先通过 hashcode 来判断两个类是否存放某个桶里,但这个桶里可能有很多类,那么我们就需要再通过 equals 来在这个桶里找到我们要的类。
那么,重写了equals(),为什么还要重写hashCode()呢?
想想,你要在一个桶里找东西,你必须先要找到这个桶啊,你不通过重写hashcode()来找到桶,光重写equals()有什么用啊
java中计算哈希值实际上是调用类的hashCode方法,进行key的相等性比较是调用key的equals方法,即hashCode负责找到位置,equals负责比较key是否相同,两个对象如果equals相同,那么hashCode一定相同,如果hashCode相同,equals不一定相同