深入HashCode方法

来源:互联网 发布:超级奇门遁甲排盘软件 编辑:程序博客网 时间:2024/05/22 15:06

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

  一个对象的HashCode就是一个简单的Hash算法的实现,虽然它和那些真正的复杂的Hash算法相比还不能叫真正的算法,它如何实现它,不仅仅是程序员的编程水平问题,而是关系到你的对象在存取是性能的非常重要的关系.有可能,不同的HashCode可能会使你的对象存取产生,成百上千倍的性能差别。

  我们先来看一下,在JAVA中两个重要的数据结构:HashMap和Hashtable,虽然它们有很大的区别,如继承关系不同,对value的约束条件(是否允许null)不同,以及线程安全性等有着特定的区别,但从实现原理上来说,它们是一致的.所以,我们只以Hashtable来说明:

  在java中,存取数据的性能,一般来说当然是首推数组,但是在数据量稍大的容器选择中,Hashtable将有比数组性能更高的查询速度.具体原因看下面的内容。

  Hashtable在存储数据时,一般先将作为key的对象的HashCode和0x7FFFFFFF做与操作,因为一个对象的HashCode可以为负数,这样操作后可以保证它为一个正整数.然后以Hashtable的长度取模,得到值对象在Hashtable中的索引。

  index = (o.hashCode() & 0x7FFFFFFF)%hs.length;这个值对象就会直接放在Hashtable的第index位置,对于写入,这和数组一样,把一个对象放在其中的第index位置,但如果是查询,经过同样的算法,Hashtable可以直接通过key得到index,从第index取得这个值对象,而数组却要做循环比较.所以对于数据量稍大时,Hashtable的查询比数据具有更高的性能。

  虽然不同对象有不同的hashcode,但不同的hashCode经过与长度的取余,就很可能产生相同的index。

  极端情况下会有大量的对象产生一个相同的索引.这就是关系Hashtable性能问题的最重要的问题:

  Hash冲突。

  常见的Hash冲突是不同key对象最终产生了相同的索引,而一种非常甚至绝对少见的Hash冲突是,如果一组对象的个数大过了int范围,而HashCode的长度只能在int范围中,所以肯定要有同一组的元素有相同的HashCode,这样无论如何他们都会有相同的索引.当然这种极端的情况是极少见的,可以暂不考虑,但是对于同的HashCode经过取模,则会产中相同的索引,或者不同的对象却具有相同的HashCode,当然具有相同的索引。

  事实上一个设计各好的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对于我们的程序性能有着生要的影响,在程序设计时应该时时加以注意。

 

转自 http://dev2dev.bea.com.cn/bbsdoc/20060807307.html

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 荣耀v10电信网速很慢怎么办 华为路由器掉线了怎么办 三星s6只识别一张卡怎么办 华为手机卡不显示了怎么办 华为账号手机卡丢了怎么办 荣耀8耗电量太快怎么办 vivo卡2不显示怎么办 电信宽带玩王者荣耀卡怎么办 联通4g玩游戏卡怎么办 华为悦盒遥控器丢了怎么办 6s不能用电信卡怎么办 iphone6电信卡无服务怎么办 魅族手机电信卡怎么办 小米手机读不出sim卡怎么办 魅蓝note6耗电快怎么办 oppo手机下载密码忘了怎么办 华为v9玩飞车卡怎么办 苹果7耳机转换器不支持怎么办 华为mate10耳机声音小怎么办 200打一年到期了怎么办 手机欠费变成2g怎么办 手机4g网用不了怎么办 手机玩王者荣耀卡怎么办 华为隐私空间密码忘记了怎么办 华为手机王者太卡怎么办 华为手机太卡怎么办呢 华为手机5x太卡!怎么办 华为手机玩游戏时太卡了怎么办? 买到华为翻新机怎么办 华为畅享8声音小怎么办 华为5a安全模式怎么办 手机跳屏怎么办金立 苹果手机触控不灵敏怎么办 华为mate8手机声音小怎么办 华为mate9相机无法对焦怎么办 新疆外地电信卡信号差怎么办 华为手机搜索不到wifi怎么办 华为浏览器恢复只有一个页面怎么办 华为手机触摸屏没反应怎么办 快递不给送上楼怎么办 华为荣耀手机声音小怎么办