重要提示:本文为博主《面试题精选-基础篇》系列之一,关注我查看更多面试题。Gitee面试题系列开源地址:https://gitee.com/mydb/interview本题难度:低共性:高equals方法和hashCode方法是Object类中的两个基本方法,它们共同判断两个对象是它相等。为什么要这样设计?究其原因,就在于“性能”二字。使用HashMap后,我们知道经过hash计算后,我们可以直接定位到某个值存放的位置,那么想象一下,如果现在要查询某个值是否在集合中呢?如果元素(存储位置)不是通过hash方法直接定位的,那么只能按照集合的顺序一个一个比较,这种顺序比较的效率明显低于hash定位方法。这是hash和hashCode的值。当我们比较两个对象是否相等时,可以先用hashCode来比较。如果比较的结果为真,那么我们可以用equals再次确认两个对象是否相等。如果比较的结果为真,则两个对象相等,否则认为两个对象不相等。这大大提高了对象比较的效率,这也是为什么Java设计使用hashCode和equals来确认两个对象是否相等的原因。那么为什么不直接使用hashCode来判断两个对象是否相等呢?这是因为不同对象的hashCode可能相同;但是hashCode不同的对象一定不相等,所以第一次使用hashCode可以快速判断对象是否相等。然而,即使了解了以上的基础知识,仍然无法解决本文的问题,即:为什么重写equals时还要重写hashCode?要弄清楚这个问题的根源,就得从这两种方法说起。1.equals方法Object类中的equals方法用于检测一个对象是否等于另一个对象。在Object类中,此方法将确定两个对象是否具有相同的引用。如果两个对象具有相同的引用,则它们必须相等。equals方法的实现源码如下:publicbooleanequals(Objectobj){return(this==obj);}通过上面的源码和equals的定义,我们可以看出在大多数情况下,平等的判断没有任何意义!例如,在Object中使用equals来比较两个自定义对象是否相等是完全没有意义的(因为无论对象是否相等,结果都是false)。这个问题可以用下面的例子来说明:publicclassEqualsMyClassExample{publicstaticvoidmain(String[]args){Personu1=newPerson();u1.setName("Java");u1.setAge(18);人u2=newPerson();u1.setName("Java");u1.setAge(18);//打印等于结果System.out.println("等于结果:"+u1.equals(u2));}}classPerson{私有字符串名称;私人年龄;publicStringgetName(){返回名称;}publicvoidsetName(Stringname){this.name=name;}publicintgetAge(){返回年龄;}publicvoidsetAge(intage){this.age=age;}}上面程序的执行结果如下图所示:因此,一般情况下,如果我们要判断两个对象是否相等,就必须重写equals方法,这也是为什么我们需要重写equals方法的原因等于方法。2、hashCode方法hashCode翻译成中文就是哈希码,它是从一个对象派生出来的一个整数值,这个值可以是任意整数,包括正数或负数。需要注意的是,哈希码是不规则的。如果x和y是两个不同的对象,则x.hashCode()和y.hashCode()基本上永远不会相同;但如果a和b相等,则a.hashCode()必须等于b.hashCode()。Object中的hashCode源码如下:publicnativeinthashCode();从上面的源码可以看出,Object中的hashCode调用了一个(native)本地方法,返回一个int类型的整数。当然,这个整数可能是正数,也可能是负数。hashCode使用相等的值hashCode必须相同例子:publicclassHashCodeExample{publicstaticvoidmain(String[]args){Strings1="Hello";Strings2="你好";字符串s3="Java";System.out.println("s1hashCode:"+s1.hashCode());System.out.println("s2hashCode:"+s2.hashCode());System.out.println("s3hashCode:"+s3.hashCode());}}上面程序的执行结果如下图所示:不同的hashCode取值也可能相同例子:publicclassHashCodeExample{publicstaticvoidmain(String[]args){Strings1="Aa";字符串s2="BB";System.out.println("s1hashCode:"+s1.hashCode());System.out.println("s2hashCode:"+s2.hashCode());}}上面程序的执行结果,如下图所示:3.为什么要一起重写?接下来回到本文的主题,为什么重写equals的时候一定要重写hashCode呢?为了说明这个问题,我们需要从下面的例子说起。3.1SetSet集合的正常使用是用来保存不同的对象,相同的对象会被Set合并,最后留下唯一的一条数据。其正常用法如下:set.add("Java");set.add("Java");set.add("MySQL");set.add("MySQL");set.add("Redis");System.out.println("设置设置长度:"+set.size());System.out.println();//打印Set中的所有元素set.forEach(d->System.out.println(d));}}以上程序执行结果如下图所示:从上面的结果可以看出,重复数据已经被Set集合“合并”了,这也是Set集合最大的特点:去重.3.2Set集合的“变态”然而,如果我们在Set集合中存储一个自定义对象,只重写了equals方法,有趣的事情就会发生,如下代码所示:importjava.util.HashSet;importjava.util.Objects;importjava.util.Set;publicclassEqualsExample{publicstaticvoidmain(String[]args){//对象1Persionp1=newPersion();p1.setName("Java");p1.setAge(18);//对象2Persionp2=newPersion();p2.setName("Java");p2.setAge(18);//创建Set集合Set
