|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有帐号?立即注册
x
通过视频学习比传统的大课堂学习更适合成人化的学习规律。有人说大课堂气氛好,学习氛围浓,热闹,可以认识很多人。每一个Java对象都有hashCode()和equals()办法。很多类疏忽(Override)这些办法的缺省实行,以在对象实例之间供应更深条理的语义可比性。在Java理念和理论这一部分,Java开辟职员BrianGoetz向您先容在创立Java类以无效和正确界说hashCode()和equals()时应遵守的划定规矩和指南。您能够在会商论坛与作者和别的读者一同切磋您对本文的意见。(您还能够点击本文顶部或底部的会商进进论坛。)
固然Java言语不间接撑持联系关系数组--可使用任何对象作为一个索引的数组--但在根Object类中利用hashCode()办法明白暗示希冀普遍利用HashMap(及其先辈Hashtable)。幻想情形下基于散列的容器供应无效拔出和无效检索;间接在对象形式中撑持散列能够增进基于散列的容器的开辟和利用。
界说对象的相称性
Object类有两种办法来揣度对象的标识:equals()和hashCode()。一样平常来讲,假如您疏忽了个中一种,您必需同时疏忽这两种,由于二者之间有必需保持的相当主要的干系。特别情形是依据equals()办法,假如两个对象是相称的,它们必需有不异的hashCode()值(只管这一般不是真的)。
特定类的equals()的语义在Implementer的左边界说;界说对特定类来讲equals()意味着甚么是其计划事情的一部分。Object供应的缺省实行复杂援用上面等式:
publicbooleanequals(Objectobj){return(this==obj);}
在这类缺省实行情形下,只要它们援用真正统一个对象时这两个援用才是相称的。一样,Object供应的hashCode()的缺省实行经由过程将对象的内存地点对映于一个整数值来天生。因为在某些架构上,地点空间年夜于int值的局限,两个分歧的对象有不异的hashCode()是大概的。假如您疏忽了hashCode(),您仍然可使用System.identityHashCode()办法来接进这类缺省值。
疏忽equals()--复杂实例
缺省情形下,equals()和hashCode()基于标识的实行是公道的,但关于某些类来讲,它们但愿放宽等式的界说。比方,Integer类界说equals()与上面相似:
publicbooleanequals(Objectobj){
return(objinstanceofInteger
&&intValue()==((Integer)obj).intValue());
}
在这个界说中,只要在包括不异的整数值的情形下这两个Integer对象是相称的。分离将不成修正的Integer,这使得利用Integer作为HashMap中的关头字是实在可行的。这类基于值的Equal办法能够由Java类库中的一切原始封装类利用,如Integer、Float、Character和Boolean和String(假如两个String对象包括不异按次的字符,那它们是相称的)。因为这些类都是不成修正的而且能够实行hashCode()和equals(),它们都能够做为很好的散列关头字。
为何疏忽equals()和hashCode()?
假如Integer不疏忽equals()和hashCode()情形又将怎样?假如我们从未在HashMap或别的基于散列的汇合中利用Integer作为关头字的话,甚么也不会产生。可是,假如我们在HashMap中利用这类Integer对象作为关头字,我们将不克不及够牢靠地检索相干的值,除非我们在get()挪用中利用与put()挪用中极为相似的Integer实例。这请求确保在我们的全部程序中,只能利用对应于特定整数值的Integer对象的一个实例。不必说,这类办法极不便利并且毛病一再。
Object的interfacecontract请求假如依据equals()两个对象是相称的,那末它们必需有不异的hashCode()值。当其辨认才能全部包括在equals()中时,为何我们的根对象类必要hashCode()?hashCode()办法地道用于进步效力。Java平台计划职员估计到了典范Java使用程序中基于散列的汇合类(CollectionClass)的主要性--如Hashtable、HashMap和HashSet,而且利用equals()与很多对象举行对照在盘算方面十分高贵。使一切Java对象都可以撑持hashCode()并分离利用基于散列的汇合,能够完成无效的存储和检索。
实行equals()和hashCode()的需求
实行equals()和hashCode()有一些限定,Object文件中枚举出了这些限定。出格是equals()办法必需显现以上司性:
Symmetry:两个援用,a和b,a.equals(b)ifandonlyifb.equals(a)
Reflexivity:一切非空援用,a.equals(a)
Transitivity:Ifa.equals(b)andb.equals(c),thena.equals(c)
ConsistencywithhashCode():两个相称的对象必需有不异的hashCode()值
Object的标准中并没有明白请求equals()和hashCode()必需分歧--它们的了局在随后的挪用中将是不异的,假定“不改动对象相称性对照中利用的任何信息。”这听起来象“盘算的了局将不改动,除非实践情形云云。”这一含混声明一般注释为相称性和散列值盘算应是对象的可断定性功效,而不是别的。
对象相称性意味着甚么?
人们很简单满意Object类标准对equals()和hashCode()的请求。决意是不是和怎样疏忽equals()除判别之外,还请求别的。在复杂的不成修值类中,如Integer(现实上是几近一切不成修正的类),选择相称分明--相称性应基于基础对象形态的相称性。在Integer情形下,对象的独一形态是基础的整数值。
关于可修正对象来讲,谜底其实不老是云云分明。equals()和hashCode()是不是应基于对象的标识(象缺省实行)或对象的形态(象Integer和String)?没有复杂的谜底--它取决于类的企图利用。关于象List和Map如许的容器来讲,人们对此争辩不已。Java类库中的年夜多半类,包含容器类,毛病呈现在依据对象形态来供应equals()和hashCode()实行。
假如对象的hashCode()值能够基于其形态举行变动,那末当利用这类对象作为基于散列的汇合中的关头字时我们必需注重,确保当它们用于作为散列关头字时,我们其实不同意变动它们的形态。一切基于散列的汇合假定,当对象的散列值用于作为汇合中的关头字时它不会改动。假如当关头字在汇合中时它的散列代码被变动,那末将发生一些不成展望和简单搅浑的了局。理论过程当中这一般不是成绩--我们其实不常常利用象List如许的可修正对象做为HashMap中的关头字。
一个复杂的可修正类的例子是Point,它依据形态来界说equals()和hashCode()。假如两个Point对象援用不异的(x,y)座标,Point的散列值来历于x和y座标值的IEEE754-bit暗示,那末它们是相称的。
关于对照庞大的类来讲,equals()和hashCode()的举动大概乃至遭到superclass或interface的影响。比方,List接口请求假如而且只要另外一个对象是List,并且它们有不异按次的不异的Elements(由Element上的Object.equals()界说),List对象即是另外一个对象。hashCode()的需求更特别--list的hashCode()值必需切合以下盘算:
hashCode=1;
Iteratori=list.iterator();
while(i.hasNext()){
Objectobj=i.next();
hashCode=31*hashCode+(obj==null?0:obj.hashCode());
}
不单单散列值取决于list的内容,并且还划定了却合各个Element的散列值的特别算法。(String类划定相似的算法用于盘算String的散列值。)
编写本人的equals()和hashCode()办法
疏忽缺省的equals()办法对照复杂,但假如不违背对称(Symmetry)或传送性(Transitivity)需求,疏忽已疏忽的equals()办法极为辣手。当疏忽equals()时,您应当老是在equals()中包含一些Javadoc正文,以匡助那些但愿可以准确扩大您的类的用户。
作为一个复杂的例子,思索以下类:
classA{
finalBsomeNonNullField;
CsomeOtherField;
intsomeNonStateField;
}
我们应怎样编写该类的equals()的办法?这类办法合用于很多情形:
publicbooleanequals(Objectother){
//Notstrictlynecessary,butoftenagoodoptimization
if(this==other)
returntrue;
if(!(otherinstanceofA))
returnfalse;
AotherA=(A)other;
return
(someNonNullField.equals(otherA.someNonNullField))
&&((someOtherField==null)
?otherA.someOtherField==null
:someOtherField.equals(otherA.someOtherField)));
}
如今我们界说了equals(),我们必需以一致的办法来界说hashCode()。一种一致但其实不老是无效的界说hashCode()的办法以下:
publicinthashCode(){return0;}
这类办法将天生大批的条目并明显下降HashMaps的功能,但它切合标准。一个更公道的hashCode()实行应当是如许:
publicinthashCode(){
inthash=1;
hash=hash*31+someNonNullField.hashCode();
hash=hash*31
+(someOtherField==null?0:someOtherField.hashCode());
returnhash;
}
注重:这两种实行都下降了类形态字段的equals()或hashCode()办法必定比例的盘算才能。依据您利用的类,您大概但愿下降superclass的equals()或hashCode()功效一部分盘算才能。关于原始字段来讲,在相干的封装类中有helper功效,能够匡助创立散列值,如Float.floatToIntBits。
编写一个完善的equals()办法是不实际的。一般,当扩大一个本身疏忽了equals()的instantiable类时,疏忽equals()是不实在际的,并且编写将被疏忽的equals()办法(如在笼统类中)分歧于为详细类编写equals()办法。关于实例和申明的更具体信息请参阅EffectiveJavaProgrammingLanguageGuide,Item7(参考材料)。
有待改善?
将散列法构建到Java类库的根对象类中是一种十分明智的计划折中办法--它使利用基于散列的容器变得云云复杂和高效。可是,人们对Java类库中的散列算法和对象相称性的办法和实行提出了很多品评。java.util中基于散列的容器十分便利和烦琐易用,但大概不合用于必要十分高功能的使用程序。固然个中年夜部分将不会改动,但当您计划严峻依附于基于散列的容器效力的使用程序时必需思索这些要素,它们包含:
太小的散列局限。利用int而不是long作为hashCode()的前往范例增添了散列抵触的概率。
糟的散列值分派。短strings和小型integers的散列值是它们本人的小整数,靠近于别的“临近”对象的散列值。一个循规导矩(Well-behaved)的散列函数将在该散列局限内更匀称地分派散列值。
无界说的散列操纵。固然某些类,如String和List,界说了将其Element的散列值分离到一个散列值中利用的散列算法,但言语标准不界说将多个对象的散列值分离到新散列值中的任何同意的办法。我们在后面编写本人的equals()和hashCode()办法中会商的List、String或实例类A利用的窍门都很复杂,但算术上还远远不敷完善。类库不供应任何散列算法的便利实行,它能够简化更先辈的hashCode()实行的创立。
当扩大已疏忽了equals()的instantiable类时很难编写equals()。当扩大已疏忽了equals()的instantiable类时,界说equals()的“不言而喻的”体例都不克不及满意equals()办法的对称或传送性需求。这意味着当疏忽equals()时,您必需懂得您正在扩大的类的布局和实行具体信息,乃至必要表露基础类中的秘密字段,它违背了面向对象的计划的准绳。
停止语
经由过程一致界说equals()和hashCode(),您能够提拔类作为基于散列的汇合中的关头字的利用性。有两种办法来界说对象的相称性和散列值:基于标识,它是Object供应的缺省办法;基于形态,它请求疏忽equals()和hashCode()。当对象的形态变动时假如对象的散列值产生变更,确信当形态作为散列关头字利用时您不同意更变动其形态。
手机用到的是用j2me所编出来的小程序。 |
|