首页 > 代码库 > 看Java中==、equals、hashCode的来龙去脉

看Java中==、equals、hashCode的来龙去脉

我有一个哥们去参加了面试,面试官这样问一个Java问题: 你说一下java对象的equals方法调用什么方法呢?我这个哥们想了想,回答说“应该是比较的引用”。听了这个答案之后,那个面试官摇头晃脑的说:“不对,你回答的不对,equals方法调用的是hashCode方法”。于是乎,我那个技术还不错的哥们就悲壮地栽在这道题目上了。

 

今天晚上,西安历史上少有的热,那就好好总结一下这个题目的来龙去脉好了,也方便给后面的朋友们提个醒,不要栽在这么简单的问题上。你说这个面试官回答的是对的么?说对也对,说不对,也是错的离谱。我窃以为有关于==equals、以及hashCode三者的关系应该经历了三个阶段,分别是:原始社会阶段、手工作坊阶段、现代工业阶段。

 

原始社会阶段

 

在混沌初开的Java第一个版本中,先知顿悟了Java面向对象的威力,于是就要创建一个所有类的祖宗。于是他大手一挥就写下了Object这个类,这个类是所有类的祖先,又代表世间万物,又啥也代表不了,这么牛哄哄的一个类。牛到了连一个属性都没有的程度,因为任何属性都不能体现这个类的特征啊!先知琢磨着,我总得给它增加几个方法吧?至少应该有一个equals方法,来表示另外一个类是不是与我相同。于是,就增加了这样一个equals类方法。

因为这个先知是从C++阵营过来的,那时候,脑子里面还浆糊着呢,比较对象的时候,还只知道用指针地址来比较。于是,就按照C++的思路,写了这个equals的具体实现,通过比较两个类的引用地址来比较是否是相同的对象。于是在Java的源代码里面,就使用了==。这样,就有了我们后面在看Object对象的时候,里面的实现居然是比较对象引用。

 

手工作坊阶段

 

随着Object对象阵营的逐步发展,很多人开始使用面向对象的思维思考问题。很多负责制造Object子孙的工匠们,开始觉着Object类的实现太过于低端,望着Object那粗俗的实现,不仅暗自垂泪,感叹世道不公,这么弱智的设计,简直有辱我们手工工匠的名声!

使用两个引用地址来比较对象,这简直是毫无用处!两个对象,就应该天然的使用对象之间的属性是否相等来进行比较的么!怎么可能使用地址?使用地址怎么能够表示两个时期的狗狗是同一个狗狗呢?

于是负责制造StringDateIntegerDouble等封装类,还有制造File的工匠们,偷偷的将Objectequals方法给覆盖了,按照内容来返回真假。这样,equals方法的实现,就往前一小步,文明一大步了。

 

现代工业阶段

 

随着Java一度的风光无俩,他所要处理的内容就变的越来越多。甚至一个对象里面可能含有成百上千,甚至几百万的数据对象。从这些对象里面来找到一个内容还真是麻烦的很,比如,现在有100W个对象中要找到一个对象,岂不是一个一个的找,最坏的情况下要找100W次?

后来,Java的设计者们开一个Scrum会议,发现他们的大学课程上有一门叫做数据结构的课程,数据结构里面有一章散列表的描述,大概的意思就是,将这100W个对象,按照某一个码值,放到100个框子里面去,这样每个框子里面只有1W个对象。如此这般,最快情况下的查找就变成了1W+1次(查找在哪个框子)了。我的乖乖,这当真是一个好办法!如果框子足够多,就能够将查找次数大大降低!

且慢要将这项技术运用到Java程序里面,会面临什么问题呢?首先,散列集是要求不能存放相同元素的,如果前后进来两个相同的元素,被分到了不同的格子里面去,那么散列集中就会保存了两个相同的元素。其次,同样是Java中的数组对象如何比较呢?因为每一个数组中的对象都是由多个对象组成的?再次,还是数组的问题,如何对数组获取散列码值呢?

经过慎重的讨论之后,就形成了下面的整改方案,并且付诸实施:

1)Object中增加hashCode方法,这个方法主要的用途就是在使用散列算法的时候,能够作为散列的依据,该方法返回的是整数值,可以为正整数,也可以为负整数。对得到的值取模之后,就可以作为放入往哪一个篮子的依据。

2)为了防止不同的篮子中被放入了不同的对象,在注视上写上了一些警告后人的约定,如果你希望重写equals,那么请务必顺带将hashCode方法重写,这样,能够保证hashCodeequals在逻辑上是一致的。

3)对于数组的问题,为了简化后人的使用,就提供了Arrays的工具类,可以通过工具类的相关方法来解决数组的获取hashCode方法,以及数组对象的比较问题。

 

经过了上面的分析,我觉着我对==equalshashCode三者的关系有了更深的了解。不管你懂不懂,反正我是懂了。