分享
 
 
 

Go deep into HashCode method

王朝java/jsp·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

Go deep into HashCode

为什么HashCode对于对象是如此的重要?

一个对象的HashCode就是一个简单的Hash算法的实现,虽然它和那些真正的复杂的

Hash算法相比还不能叫真正的算法,它如何实现它,不仅仅是程序员的编程水平问题,

而是关系到你的对象在存取是性能的非常重要的关系.有可能,不同的HashCode可能

会使你的对象存取产生,成百上千倍的性能差别.

我们先来看一下,在JAVA中两个重要的数据结构:HashMap和Hashtable,虽然它们有很

大的区别,如继承关系不同,对value的约束条件(是否允许null)不同,以及线程安全性

等有着特定的区别,但从实现原理上来说,它们是一致的.所以,我们只以Hashtable来

说明:

在java中,存取数据的性能,一般来说当然是首推数组,但是在数据量稍大的容器选择中,

Hashtable将有比数据性能更高的查询速度.具体原因看下面的内容.

Hashtable在存储数据时,一般先将该对象的HashCode和0x7FFFFFFF做与操作,因为一个

对象的HashCode可以为负数,这样操作后可以保证它为一个正整数.然后以Hashtable的

长度取模,得到该对象在Hashtable中的索引.

index = (o.hashCode() & 0x7FFFFFFF)%hs.length;

这个对象就会直接放在Hashtable的每index位置,对于写入,这和数据一样,把一个对象

放在其中的第index位置,但如果是查询,经过同样的算法,Hashtable可以直接从第index

取得这个对象,而数组却要做循环比较.所以对于数据量稍大时,Hashtable的查询比数据

具有更高的性能.

既然一个对象可以根据HashCode直接定位它在Hashtable中的位置,那么为什么Hashtable

还要用key来做映射呢?这就是关系Hashtable性能问题的最重要的问题:Hash冲突.

常见的Hash冲突是不同对象最终产生了相同的索引,而一种非常甚至绝对少见的Hash冲突

是,如果一组对象的个数大过了int范围,而HashCode的长度只能在int范围中,所以肯定要

有同一组的元素有相同的HashCode,这样无论如何他们都会有相同的索引.当然这种极端

的情况是极少见的,可以暂不考虑,但是对于同的HashCode经过取模,则会产中相同的索引,

或者不同的对象却具有相同的HashCode,当然具有相同的索引.

所以对于索引相同的对象,在该index位置存放了多个值,这些值要想能正确区分,就要依

靠key来识别.

事实上一个设计各好的HashTable,一般来说会比较平均地分布每个元素,因为Hashtable

的长度总是比实际元素的个数按一定比例进行自增(装填因子一般为0.75)左右,这样大多

数的索引位置只有一个对象,而很少的位置会有几个元素.所以Hashtable中的每个位置存

放的是一个链表,对于只有一个对象是位置,链表只有一个首节点(Entry),Entry的next为

null.然后有hashCode,key,value属性保存了该位置的对象的HashCode,key和value(对象

本身),如果有相同索引的对象进来则会进入链表的下一个节点.如果同一个索引中有多个

对象,根据HashCode和key可以在该链表中找到一个和查询的key相匹配的对象.

从上面我看可以看到,对于HashMap和Hashtable的存取性能有重大影响的首先是应该使该

数据结构中的元素尽量大可能具有不同的HashCode,虽然这并不能保证不同的HashCode

产生不同的index,但相同的HashCode一定产生相同的index,从而影响产生Hash冲突.

对于一个象,如果具有很多属性,把所有属性都参与散列,显然是一种笨拙的设计.因为对象

的HashCode()方法几乎无所不在地被自动调用,如equals比较,如果太多的对象参与了散列.

那么需要的操作常数时间将会增加很大.所以,挑选哪些属性参与散列绝对是一个编程水平

的问题.

从实现来说,一般的HashCode方法会这样:

return Attribute1.HashCode() + Attribute1.HashCode()..[+super.HashCode()],

我们知道,每次调用这个方法,都要重新对方法内的参与散列的对象重新计算一次它们的

HashCode的运算,如果一个对象的属性没有改变,仍然要每次都进行计算,所以如果设置一

个标记来缓存当前的散列码,只要当参与散列的对象改变时才重新计算,否则调用缓存的

hashCode,这可以从很大程度上提高性能.

默认的实现是将对象内部地址转化为整数作为HashCode,这当然能保证每个对象具有不同

的HasCode,因为不同的对象内部地址肯定不同(废话),但java语言并不能让程序员获取对

象内部地址,所以,让每个对象产生不同的HashCode有着很多可研究的技术.

如果从多个属性中采样出能具有平均分布的hashCode的属性,这是一个性能和多样性相矛

盾的地方,如果所有属性都参与散列,当然hashCode的多样性将大大提高,但牺牲了性能,

而如果只能少量的属性采样散列,极端情况会产生大量的散列冲突,如对"人"的属性中,如

果用性别而不是姓名或出生日期,那将只有两个或几个可选的hashcode值,将产生一半以上

的散列冲突.所以如果可能的条件下,专门产生一个序列用来生成HashCode将是一个好的选

择(当然产生序列的性能要比所有属性参与散列的性能高的情况下才行,否则还不如直接用

所有属性散列).

如何对HashCode的性能和多样性求得一个平衡,可以参考相关算法设计的书,其实并不一定

要求非常的优秀,只要能尽最大可能减少散列值的聚集.重要的是我们应该记得HashCode对

于我们的程序性能有着生要的影响,在程序设计时应该时时加以注意.

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有