同时重写equals和hashCode的意义

时间:2021-08-05 16:02:03

使用过eclipse等IDE集成环境的朋友都知道, 当我们需要判断两个对象是否相等时, 往往会想到重写 equals 方法。为此, eclipse代码自动生成工具还提供了非常便捷的功能(右健->Source->Generate hashCode() and equals() )。大家都容易理解equals() 方法的意义, 它提供给java用于判断两个对象的“值”是否相等的方法(如果不重写并且对象是直接继承自Object类,java则仅仅是判断两个对象的引用是否相等)。但是,至于eclipse为什么还要同时生成hashCode呢?相信不少Java程序员还是有点一头雾水,那么我们来简单分析一下!  

首先,我们来看一下默认的hashCode() 是如何实现的。Object类的hashCode方法返回的是基于对象地址所构造的int值。那么,两个不同引用地址的对象,它们的地址不相同,故hashCode()返回的整形数值也是不同的——即使子类覆盖了Object的equals方法(因为hashCode的实现不依赖于equals方法, 修改equals方法不会对hashCode产生影响)。那么,hashCode又是用于神马场合呢?hashCode主要用于Set集合(与之对应的是List,List允许重复对象),而我们知道Set集合的内部是不允许出现重复对象的!那么,Java是根据什么来判断Set集合内的对象是否相等呢?很多朋友可能会马上想到equals方法——的确,equals可以做到,当要插于一个新对象时,调用对象的equals方法作用于Set内部的所有元素,即可判断该对象是否已经存在于Set集合中。

然而,这里有一个相当明显的效率问题,当Set集合中有成千上万个元素时,java岂不是要成千上万次调用equals方法?!显然,大神级的Java语言设计者是知道这个问题的,他们用hash算法设计了Set的一种实现,基本原理是:运用一种映射函数,将一个整形数值映射到一块内存地址,而该地址将存放add进来的对象的引用,而这个整形数值,由对象的hashCode()方法获得。这样,当新元素进入时,java就不需要成千上万遍地调用集合对象里的equals方法了,java将根据待加对象的hashCode,根据既定的映射函数,直接找到该hashCode “专属” 的内存地址,将对象引用写到到该地址。因此,对象的hashCode()返回值是对象在基于散列表集合内部的“身份证”,java根据该身份证来判断Set内的对象是否唯一的。

明白了上面关于Set的实现原理,我们就可以理解在重写equals时同时重写hashCode的意义了:如果不重写hashCode,当插入一个新对象进入HashSet(或者其他基于散列算法的集合类对象),即便集合内已经存在了一个在调用equals时返回为true的对象,由于新对象的引用地址不同,其hashCode也和集合内部那个“值”相等的对象的hashCode不同,这样,java就会在另一个内存空间写下新元素的引用地址。这样,从逻辑上看,Set集合就存在两个“值”完全相等的元素了,这本身就违背了Set集合的概念(Set不允许出现重复对象)!

因此,在重写equals时,请务必重写hashCode方法,并且要保证:当两个对象equals时,它们的hashCode也必须是相同的;当不equals,它们的hashCode也是不同的!不过大家不需要费力去想hashCode的实现了,eclipse可以帮助我们自动生成基于类成员变量的equals和hashCode方法对:)

所以,在重写类的equals方法时,记得要重写hashCode方法哦!这样,你的对象才不会在散列表内产生冲突,这样,你才能赶上java语言大神级设计者的节奏!