java中为何重写equals时必须重写hashCode方法详解
前言
大家都知道,equals和hashcode是java.lang.Object类的两个重要的方法,在实际应用中常常需要重写这两个方法,但至于为什么重写这两个方法很多人都搞不明白。
在上一篇博文Java中equals和==的区别中介绍了Object类的equals方法,并且也介绍了我们可在重写equals方法,本章我们来说一下为什么重写equals方法的时候也要重写hashCode方法。
先让我们来看看Object类源码
/** *Returnsahashcodevaluefortheobject.Thismethodis *supportedforthebenefitofhashtablessuchasthoseprovidedby *{@linkjava.util.HashMap}. **Thegeneralcontractof{@codehashCode}is: *
-
*
- Wheneveritisinvokedonthesameobjectmorethanonceduring *anexecutionofaJavaapplication,the{@codehashCode}method *mustconsistentlyreturnthesameinteger,providednoinformation *usedin{@codeequals}comparisonsontheobjectismodified. *Thisintegerneednotremainconsistentfromoneexecutionofan *applicationtoanotherexecutionofthesameapplication. *
- Iftwoobjectsareequalaccordingtothe{@codeequals(Object)} *method,thencallingthe{@codehashCode}methodoneachof *thetwoobjectsmustproducethesameintegerresult. *
- Itisnotrequiredthatiftwoobjectsareunequal *accordingtothe{@linkjava.lang.Object#equals(java.lang.Object)} *method,thencallingthe{@codehashCode}methodoneachofthe *twoobjectsmustproducedistinctintegerresults.However,the *programmershouldbeawarethatproducingdistinctintegerresults *forunequalobjectsmayimprovetheperformanceofhashtables. *
*Asmuchasisreasonablypractical,thehashCodemethoddefinedby *class{@codeObject}doesreturndistinctintegersfordistinct *objects.(Thisistypicallyimplementedbyconvertingtheinternal *addressoftheobjectintoaninteger,butthisimplementation *techniqueisnotrequiredbythe *Java™programminglanguage.) * *@returnahashcodevalueforthisobject. *@seejava.lang.Object#equals(java.lang.Object) *@seejava.lang.System#identityHashCode */ publicnativeinthashCode();
/** *Indicateswhethersomeotherobjectis"equalto"thisone. **The{@codeequals}methodimplementsanequivalencerelation *onnon-nullobjectreferences: *
-
*
- Itisreflexive:foranynon-nullreferencevalue *{@codex},{@codex.equals(x)}shouldreturn *{@codetrue}. *
- Itissymmetric:foranynon-nullreferencevalues *{@codex}and{@codey},{@codex.equals(y)} *shouldreturn{@codetrue}ifandonlyif *{@codey.equals(x)}returns{@codetrue}. *
- Itistransitive:foranynon-nullreferencevalues *{@codex},{@codey},and{@codez},if *{@codex.equals(y)}returns{@codetrue}and *{@codey.equals(z)}returns{@codetrue},then *{@codex.equals(z)}shouldreturn{@codetrue}. *
- Itisconsistent:foranynon-nullreferencevalues *{@codex}and{@codey},multipleinvocationsof *{@codex.equals(y)}consistentlyreturn{@codetrue} *orconsistentlyreturn{@codefalse},providedno *informationusedin{@codeequals}comparisonsonthe *objectsismodified. *
- Foranynon-nullreferencevalue{@codex}, *{@codex.equals(null)}shouldreturn{@codefalse}. *
*The{@codeequals}methodforclass{@codeObject}implements *themostdiscriminatingpossibleequivalencerelationonobjects; *thatis,foranynon-nullreferencevalues{@codex}and *{@codey},thismethodreturns{@codetrue}ifandonly *if{@codex}and{@codey}refertothesameobject *({@codex==y}hasthevalue{@codetrue}). *
*Notethatitisgenerallynecessarytooverridethe{@codehashCode} *methodwheneverthismethodisoverridden,soastomaintainthe *generalcontractforthe{@codehashCode}method,whichstates *thatequalobjectsmusthaveequalhashcodes. * *@paramobjthereferenceobjectwithwhichtocompare. *@return{@codetrue}ifthisobjectisthesameastheobj *argument;{@codefalse}otherwise. *@see#hashCode() *@seejava.util.HashMap */ publicbooleanequals(Objectobj){ return(this==obj); }
hashCode:是一个native方法,返回的是对象的内存地址,
equals:对于基本数据类型,==比较的是两个变量的值。对于引用对象,==比较的是两个对象的地址。
接下来我们看下hashCode的注释
1.在Java应用程序执行期间,在对同一对象多次调用hashCode方法时,必须一致地返回相同的整数,前提是将对象进行equals比较时所用的信息没有被修改。
从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
2.如果根据equals(Object)方法,两个对象是相等的,那么对这两个对象中的每个对象调用hashCode方法都必须生成相同的整数结果。
3.如果根据equals(java.lang.Object)方法,两个对象不相等,那么两个对象不一定必须产生不同的整数结果。
但是,程序员应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能。
从hashCode的注释中我们看到,hashCode方法在定义时做出了一些常规协定,即
1,当obj1.equals(obj2)为true时,obj1.hashCode()==obj2.hashCode()
2,当obj1.equals(obj2)为false时,obj1.hashCode()!=obj2.hashCode()
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。如果我们将对象的equals方法重写而不重写hashcode,当我们再次new一个新的对象的时候,equals方法返回的是true,但是hashCode方法返回的就不一样了,如果需要将这些对象存储到结合中(比如:Set,Map...)的时候就违背了原有集合的原则,下面让我们通过一段代码看下。
/** *@seePerson *@paramargs */ publicstaticvoidmain(String[]args) { HashMapmap=newHashMap (); Personp=newPerson("jack",22,"男"); Personp1=newPerson("jack",22,"男"); System.out.println("p的hashCode:"+p.hashCode()); System.out.println("p1的hashCode:"+p1.hashCode()); System.out.println(p.equals(p1)); System.out.println(p==p1); map.put(p,888); map.put(p1,888); map.forEach((key,val)->{ System.out.println(key); System.out.println(val); }); }
equals和hashCode方法的都不重写
publicclassPerson { privateStringname; privateintage; privateStringsex; Person(Stringname,intage,Stringsex){ this.name=name; this.age=age; this.sex=sex; } }
p的hashCode:356573597 p1的hashCode:1735600054 false false com.blueskyli.练习.Person@677327b6 com.blueskyli.练习.Person@1540e19d
只重写equals方法
publicclassPerson { privateStringname; privateintage; privateStringsex; Person(Stringname,intage,Stringsex){ this.name=name; this.age=age; this.sex=sex; } @Overridepublicbooleanequals(Objectobj) { if(objinstanceofPerson){ Personperson=(Person)obj; returnname.equals(person.name); } returnsuper.equals(obj); } }
p的hashCode:356573597 p1的hashCode:1735600054 true false com.blueskyli.练习.Person@677327b6 com.blueskyli.练习.Person@1540e19d
equals和hashCode方法都重写
publicclassPerson { privateStringname; privateintage; privateStringsex; Person(Stringname,intage,Stringsex){ this.name=name; this.age=age; this.sex=sex; } @Overridepublicbooleanequals(Objectobj) { if(objinstanceofPerson){ Personperson=(Person)obj; returnname.equals(person.name); } returnsuper.equals(obj); } @OverridepublicinthashCode() { returnname.hashCode(); } }
p的hashCode:3254239 p1的hashCode:3254239 true false com.blueskyli.练习.Person@31a7df
我们知道map是不允许存在相同的key的,由上面的代码可以知道,如果不重写equals和hashCode方法的话会使得你在使用map的时候出现与预期不一样的结果,具体equals和hashCode如何重写,里面的逻辑如何实现需要根据现实当中的业务来规定。
总结:
1,两个对象,用==比较比较的是地址,需采用equals方法(可根据需求重写)比较。
2,重写equals()方法就重写hashCode()方法。
3,一般相等的对象都规定有相同的hashCode。
4,String类重写了equals和hashCode方法,比较的是值。
5,重写hashcode方法为了将数据存入HashSet/HashMap/Hashtable(可以参考源码有助于理解)类时进行比较
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。