首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

【第8条】改写equals时老是要改写hashCode

2012-10-26 
【第8条】改写equals时总是要改写hashCode??? 一个很常见的错误根源在于没有改写hashCode方法。在每一个改写

【第8条】改写equals时总是要改写hashCode

??? 一个很常见的错误根源在于没有改写hashCode方法。在每一个改写了equals的方法的类中,你必须也要改写hashCode方法。如果不这么做的话,就会违反Object.hashCode的通用约定,从而导致该类无法与所有基于散列值(hash)的集合类结合在一起正常运行,这样的集合类包括HashMap、HashSet、Hashtable。


??? 可能有人会问:什么是hash?它是干什么用的?


??? 的确,一开始我也没有在意到hashCode。翻了一下底层API的代码,我自己的类确实也犯了这个错误,而且还发现其他项目组的API也是。那个项目是从Java1.0版本就做起的,直到现在使用的是1.5,但是他们说了:我们改写了equals的类只是一些JavaBean,他们很少有机会(不太严谨的说是从不会)被放到HashMap、HashSet、Hashtable中,就更不会放进去而且还当Key用了。


??? 那么,还是回答一下刚才的问题吧,什么是hash?它是干什么用的?如果违反了此条到底在什么情况下,会有怎样的后果?


??? hash是一个散列值,或者叫“杂乱编码”,对一个对象做hashCode()运算,简单地说就是给这个对象算出一个无规则的ID。我们知道,数组的优势在于遍历,而HashMap等的优势在于快速查找。当我们在HashMap中放入了一些Key-Vaule的值对后,可以通过Key值很快地检索出它所对应的Value(很像对数据库表的查询),关键的是这个检索的时间耗费是固定的(严谨地说应该是与容量无关的),而非与内容多少线性的。很明显它不是像数组一样循环遍历、比较来做查找的,而是直达目标。那么它是如何做到的呢?


??? 这里再多啰嗦几句,HashMap的原理:当你初始化一个HashMap时,系统会预先开创一些空间用于放置将要被放入的对象,之后随着对象的放入,当容量不够用的时候就将容积扩倍。但是,什么时候叫“不够用”呢?并不是现有的“格子”都被放满了,而是75%(这个百分比叫loadFactor,Java用的是0.75,其它系统可能不一样,微软好像是0.72)。这是为什么呢?其实这个百分比是可以自己指定的,但是如果没有特别的情况,建议你不要这么做,相信很多数学专家已经经过大量的计算和论证才选用的0.75这个值的。再多说一句,其实所谓“扩倍”也是不严格的说法,各个系统在选择容量上都有自己的策略,比如Java是不小于扩倍数的一个奇数,而微软好像是用质数。再有就是容量的初始值各系统也不相同,Java是11。


??? 当一个Key-Value值对被put进来时,首先计算Key的hashCode,然后对容量取余,这个余数就是这个值对将要被存放的位置。get的时候,也是首先对Key算hashCode,然后对容量取余数,之后直接到余数的位置就找的目标了(那么如果在set和get之间,这个HashMap扩容过,那么该如何呢?这就不在这里详细讨论了)。这就是为什么检索的时间耗费是与容量无关的了。但是,两个不同的对象也可能具有相同的hashCode,或者两个具有不同hashCode的对象恰巧它们的HashCode之差是容量的整数倍,这样都会导致它们得到的余数是相同的,就又怎么办呢?HashMap会在每个位置上其实都是一个链表,如果有两个以上的对象落在了相同的位置上,那么就让链表上第一个元素的next指向下一个元素即可(如果某位置上仅有一个元素,那它的next就是null),这就是为什么严谨地说不能是“时间耗费是固定的”,在链表上查找还是需要时间一个一个的遍历比较的。现在我们知道了吧,即使约定并不要求通过equals方法判断是不相等的两个对象的hashCode也一定不能相同,但是最好还是让他们不同的好。如果这样一个hashCode()算法:

?

    public int hashCode(){      int result = 17;      result = 37 * result + Float.floatToIntBits(this.re);      result = 37 * result + Float.floatToIntBits(this.im);      return result;    }

?

?

?

【Effective Java 学习笔记】系列连载专题请见:
http://tonylian.iteye.com/categories/64208

?

1 楼 lxh2002 2009-05-12   写得不错,分析都挺详细的。

热点排行