hashCode和equals函数详解
一、函数简介:
首先,hashCode()和equals()函数都是父类object中public修饰的,可供子类重写的方法。hashCode函数是native修饰的,返回值是int型当前对象的映射地址,equals默认的比较方式==号,即基本数据类型比较数值,引用数据类型比较地址引用。
区别是,hashCode函数的返回值是int型,返回的是类对象的映射地址。而equals()函数的返回值是boolean型的,参与比较的两个对象,内容相同就为true,内容不同就为false。
二、hashMap中的应用
在hashMap中,有个static final int hash(Object key)函数,通过调用key.hashCode()函数,获得key的hash值。当往里面put数据时,通过此key的hash值(&)与运算上表长减一,获得此key对应在表中存放的下标位置,然后在调用equals函数判断value的具体内容,判断是否存在重复的key。(这里的equals之所以从原来默认的,通过“==” 号比较引用数据的地址引用,变为这里的比较引用数据类型Node节点对象中的内容,是因为hashMap的内置类Node类中,重写了equals函数,重新定义了比较规则,通过比较key和value的具体内容来判断是否为同一对象,而非之前Object中默认的通过“ ==”来比较Node对象的地址引用。)
hashMap源码解析:
在HashMap类源码中,有个static final int hash(Object key)函数,通过调用key.hashCode()方法,获得key的hash值(若我们自行重写了hashMap<Key,Value>中Key的hashCode()方法,那么此时key.hashCode()方法实际执行的是我们自行重写的hashCode()方法)。
在HashMap的内置类Node类中,重写了equals()函数,重新定义了比较规则,通过比较key和value的具体内容来判断是否为同一对象,而非之前Object中默认的通过“ ==”来比较Node对象的地址引用。此外,此内置类Node中还重写了hashCode()方法。
当往hashMap里面put数据时,通过此key的hash值(&)与运算上表长减一,获得此key应在表中存放的下标位置,然后在调用equals函数判断value的具体内容,判断是否存在重复的key。
三、需要重写的原因:
在实际应用中,我们平时需要重写hashCode()和equals()的情况,通常是因为我们要把我们自定义的引用数据类型存入到hashSet或者treeSet中。如果我们在我们的自定义类中重写了hashCode()和equals()函数,由于hashSet的底层是通过hashMap实现的(利用hashMap中key的不重复特性,将我们存入的数据作为key,以实现去重复的目的)。所以当我们把我们自定义的类对象put进hashSet中时,底层调用的是hashMap的put函数,就执行了我们上面说的流程。先通过hash(Object key)函数调用key.hashCode()获得key的hash值,当往里面put数据时,通过此key的hash值(&)与运算上表长减一,获得此键值对应在表中存放的下标位置,然后在调用equals函数判断value的具体内容,判断是否存在重复的key。
由于我们所有的类都会默认继承父类Object类,所以我们可以重写Object类中的hashCode()和equals()函数。通过继承规则我们也知道,如果我们自定义的子类中重写了hashCode()和equals()函数,那么在调用这两个函数时,会调用我们子类重写的函数逻辑。由于hashCode()和equals()函数的作用以及使用场景,我们可以知道,hashCode函数的作用,是用来定义,通过当前这个类创建类对象时,这个类对象的hash值到底是应该怎样计算的。而equals函数的作用,是用来规定,通过当前这个类创建类对象时,这个类对象在什么情况下可以认为是同一个对象,比如,我们认为,当前类中某些字段的内容完全相同时,我们就认为它们是同一个对象,而不再是之前的,通过==号类判断当前类对象的地址引用。
四、代码示例:
自定义了一个Person类,类中只有一个name属性。
我的需求是,只要person对象的name字段中的内容相同,我就认为这是同一个对象,只在hashSet中存储一份。所以,我需要重写hashCode()和equals()这两个函数,重新定义属于我的比较规则。
equals()函数的具体内容如下
hashCode()函数的具体内容如下
为了进行对比,我又自定义了一个什么都没有重写的Dog类,内容如下:
main函数如下:
执行结果如下:
可以看到,Person类中由于重写了hashCode()和equals()函数,当name属性的内容相同时,会被认为是同一个对象。
而Dog类中由于没有重写hashCode()和equals()函数,所以即使name属性相同,由于地址空间不同,也会被认为是不同的对象。