Hashcode
1.什么是hashcode?Hashcode是怎么来的?
2.为什么要有hashcode?Hashcode用来干嘛?
要了解hashcode是什么,先来介绍一下hash函数,hash表的大概
Hash是一类函数,通过该函数的算法实现来得到一个hash值。需要注意的是hash函数不是具体某一个函数,而是指某一类函数或者说算法。这类算法简单来说就是把数据进行压缩分类。由于通常要把一堆很大的数据压缩成一段比较小的数据,所以看起来有点像分组。(比如把1000个数据,通过算法,压缩进100个数据空间里),然后就用这100个数据来代表这1000个数据。从这就可以看出来那100个数据每个平均代表的肯定不止一个,这就是hash冲突,后面再说。
这个时候,我们就需要知道另一个东西,hash表,通过hash算法得到的hash值就在这张hash表中,也就是说,hash表就是所有的hash值组成的,有很多种hash函数,也就代表着有很多种算法得到hash值,如上面截图的三种,等会我们就拿第一种来说
3.Hashcode
Hashcode就是通过hash函数得来的。 hashcode就是在hash表中的位置。(这里需要注意的是hashcode不是一直不变的,它是根据你hash算法的定义,hashcode的值会有所改变的)。
通常一开始每个对象都会有一个默认的hash函数来计算这个对象的hashcode。一般一开始的一个对象的hashcode是根据这个对象的物理地址转换成的一个整数,然后再用这个整数通过hash函数,就得到
了一个hashcode的值(再强调,这个hashcode的值会根据hash函数的不同而不同的)。
所以,hashcode是什么呢?就是在hash表中对应的位置。这里如果还不是很清楚的话,举个例子,hash表中有 hashcode为1、hashcode为2、(…)3、4、5、6、7、8这样八个位置,有一个对象A,A的物理地址转换为一个整数17(这是假如),就通过直接取余算法,17%8=1,那么A的hashcode就为1,且A就在hash表中1的位置。肯定会有其他疑问,接着看下面,这里只是举个例子来让你们知道什么是hashcode的意义。
二、Hashcode是用来干嘛的?
前面讲了一些hashcode怎么来的,是什么。那么大家可以想一下为什么要有hashcode?上面说过了,hash算法的作用是把一大堆数据压缩成用一小堆数据来表示。
1、 HashCode的存在主要是为了查找的快捷性,HashCode是用来在散列存储结构中确定对象的存储地址的(后半句说的用hashcode来代表对象就是在hash表中的位置)
**2、 为什么hashcode就查找的更快,**比如:我们有一个能存放1000个数这样大的内存中,在其中要存放1000个不一样的数字,用最笨的方法,就是存一个数字,就遍历一遍,看有没有相同得数,当存了900个数字,开始存901个数字的时候,就需要跟900个数字进行对比,这样就很麻烦,很是消耗时间,用hashcode来记录对象的位置,来看一下。hash表中有1、2、3、4、5、6、7、8个位置,存第一个数,hashcode为1,该数就放在hash表中1的位置,存到100个数字,hash表中8个位置会有很多数字了,1中可能有20个数字,存101个数字时,他先查hashcode值对应的位置,假设为1,那么就有20个数字和他的hashcode相同,他只需要跟这20个数字相比较(equals),如果每一个相同,那么就放在1这个位置,这样比较的次数就少了很多,实际上hash表中有很多位置,这里只是举例只有8个,所以比较的次数会让你觉得也挺多的,实际上,如果hash表很大,那么比较的次数就很少很少了。 通过对原始方法和使用hashcode方法进行对比,我们就知道了hashcode的作用,并且为什么要使用hashcode了
3、看见这个是不是有点熟悉?其实它有点像我们的分班,分组。这些值就像身上贴着自己的名字,但是不能说话。假如有一千人,如果我要找某一个人,没有分班分组前,我是不是要一个一个地去找?去看他们的名字?这谁顶得住? 那么分好班分好组就相当于在他们的名字前面加了分班分组信息,好了这下,我要找某个人,我只要先找到它的班级,然后找到他们的组。假设有10个班,每个班有10个组,那么我找到分组后,只需要找10个人就可以找到我想找那个了。
(10个班10个组那肯定是比较平均,好的分配了。其实hash函数的功能也是这样,尽量平均地分配好输入的一堆值。)不然,你一个班有10个分组,但是前9个分组都只有1个人,最后那个分组却有91个人。如果我要查的那个人就在这91个人的分组里,我岂不是也要查得吐血。
三、equals方法与hashcode的关系
大家上学时,老师经常会强调重写了equals方法后,一定要重写hashcode方法。我们老师也说过,但当时对这些概念不懂,好像也不妨碍写其它程序,就没放在心上。首先,我们要知道hashcode的定义(注意:这是定义,就是要你这样实现的,不管你用什么方法,如果这个定义不成立的话,那后面基于hashcode的类型(比如hashmap)这些也就不成立了)
hashCode 的常规协定是:
1.在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
2.如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。
3.以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。
4.实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。) 当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
简单地总结一下就是说:
1.如果两个对象用equals判断相等,那么他们的hashcode一定相等。
2.反过来就不用一定成立了,如果两个对象的hashcode相等,不代表这两个对象就相同或者说也不用equals了,只能说明这两个对象是放在同一组的。
先说一下Hash冲突:
上面我说过,这个hash函数是把一大堆数据压缩映射成一小堆数据的。比如1000个压缩成100个。那么这100个数据中必定会有一些数据是一个代表多个的(这就是hash冲突)。Hash冲突是必然的,因为想要不冲突的话,就这100个数据每一个数据都只代表1000数据中的一个数据。那这样这1000个数据就无法被100个数据映射完了,所以说必然会冲突的。
接着说一下上面的第二点,假如有100个数据放进1,2,3,4,5,6,7,8这8个桶里,这100个数据的hashcode怎么求?(上面说过,hashcode是根据hash函数得来的,只要它符合上面对hashcode的定义就行,他并不唯一)。就是说,一开始每个对象都有一个hashcode,那是根据内存地址求出来的,那个hashcode是相对于整个项目来说的。那这里的hashcode就是相对于这8个桶来说的。我们用最常用的一种hash函数的求法,对这8个数求模,求出来的就是一个hashcode。那好我用1%8,9%8,17%8,你会发现它们的结果都是1,也就是说它们的hashcode都是1.那他们的hashcode是不是相等? 他们自身相等?不相等。那这几个数有什么共同点?求出来的1是用来干嘛的? 就是用来放在同一个桶里的(也就是同一组)。这就是对第二点的解释了。
四、为什么equals重写了,hashcode一般也要重写?
如果大家想清楚hashcode上面两个定义的话,那对这里的理解也就不难了。
大家看这个类,他重写equals方法,好,我们来构造他的两个对象,a1,a2.这时候我们用a1.equals(a2),这成不成立?看A类的equals函数,它只需要这两个对象都是A类,并且q,w属性相等就行了。那a1,a2满不满足这些条件?显然满足。好了,提到equals千万别忘了hashcode。没有重写hashcode()函数前,a1,a2的hashcode相不相等?不相等。为什么不相等?上面说过,一开始对象的hashcode是怎么求的?是根据内存地址求的。那这两个对象的内存地址是显然不同的。(当然,他们有可能发生hash冲突,分在同一个组,这个概念少到可以忽略)。那这两个对象的hashcode不同啊,但是他们是equals的啊,显然违背了hashcode的定义啊。这就是为什么要改写equals方法也要改hashcode方法的原因了。Hashcode的改写通常也有默认的方法,这个我也没去详细了解过,好像是用了什么素数的吧
那么就有人可能会有疑问,既然每个对象一开始的hashcode不同,那么不改写equals方法的话,那岂不是没有对象是equals的? 那么我们来看一下,没有重写的equals的方法是怎么样的?
对象的默认的equals方法是这样的呀,它比较的就是地址呀。我hashcode是根据地址,equals也是根据地址,这不是很契合嘛。
当然一般的很多对象都会重写了equals方法的。比如String类,它就重写了equals,所以不用只是判断地址了。(关于String,int之类的地址之类,需要自己去了解。)
****最后总结一下,****hashcode就是用hash函数压缩后得出来的一些数字,它是用来映射
那一堆输出数据的(100映射1000).他的作用呢有点类似分组,把一大堆数据分成一组组的,再根据组号查找,这样就方便,快捷很多了,不用一个个查询。还有就是要记住hashcode的定义,hashcode与equals方法的爱恨情仇。那就明白了为什么重写equals方法还要重写hashcode。 那么hashcode和equals对应后有什么用呢?就在下一章hashmap里面讲了。其实就是你用hashcode找到某一个分组后,你还要找到某一个具体的,这时候就要equals了。
这里还是要略微提一下,hashcode用来干嘛?方便查找equals的两个对象啊,hashmap的结构是怎么样的?数组加链表,也就是说你要先找到数组的下标(indexFor()函数,用hashcode对数组长度求模,其实我觉得这也属于一个hashcode),然后再用equals逐一比较链表的值,那假如是上面那个100个数据放8个桶的例子(数据不重复放),我1放进了第一个桶,我再放进一个1进来的时候,我要查找1是否已经有了。按照惯例,1放进来要用hashcode对8求模,我之前那个1的hashcode是1,所以1%8是放进了第一个桶的,但是这个1你却告诉我它的hashcode是3,我用这个3%8,然后我就会去第三个桶中查找是否有1,显然没有。但事实上有1的,只是放在了第一个桶里,你桶都找错了,还怎么比较呀。
注意::我们通常所说的hashcode是指对象自带就是根据内存地址生成的)或者重写的hashcode((我以为这是狭义的hashcode,广义的应该是通过hash函数得到的)。基本上后面的hash函数都是基于这个hashcode来生成新的hashcode的。
参考博客:https://www.cnblogs.com/whgk/p/6071617.html