zt:從HashCode出發(fā)介紹HashMap以及如何重寫自己的HashCode方法 大家都知道,在Java里對對象的操作是基于引用的,。而當我們需要對一組對象操作的時候,,就需要有接收這一組引用的容器。平時我們最常用的就是數(shù)組,。在Java里可以定義一個對象數(shù)組來完成許多操作,。可是,,數(shù)組長度是固定的,,如果我們需要更加靈活的解決方案該怎么辦呢? Java提供了container classes來解決這一問題,。container classes包括兩個部分:Collection和Map,。 本文重點介紹HashMap。首先介紹一下什么是Map,。在數(shù)組中我們是通過數(shù)組下標來對其內(nèi)容索引的,,而在Map中我們通過對象來對對象進行索引,用來索引的對象叫做key,其對應的對象叫做value,。在下文中會有例子具體說明,。 再來看看HashMap和TreeMap有什么區(qū)別。HashMap通過hashcode對其內(nèi)容進行快速查找,,而TreeMap中所有的元素都保持著某種固定的順序,,如果你需要得到一個有序的結(jié)果你就應該使用TreeMap(HashMap中元素的排列順序是不固定的)。 下面就要進入本文的主題了,。先舉個例子說明一下怎樣使用HashMap: import java.util.*; public class Exp1 { public static void main(String[] args){ HashMap h1=new HashMap(); Random r1=new Random(); for(int i=0;i<1000;i++){ Integer t=new Integer(r1.nextInt(20)); if(h1.containsKey(t)) ((Ctime)h1.get(t)).count++; else h1.put(t, new Ctime()); } System.out.println(h1); } } class Ctime{ int count=1; public String toString(){ return Integer.toString(count); } } 在HashMap中通過get()來獲取value,,通過put()來插入value,ContainsKey()則用來檢驗對象是否已經(jīng)存在,??梢钥闯觯虯rrayList的操作相比,,HashMap除了通過key索引其內(nèi)容之外,,別的方面差異并不大。 前面介紹了,,HashMap是基于HashCode的,在所有對象的超類Object中有一個HashCode()方法,,但是它和equals方法一樣,,并不能適用于所有的情況,這樣我們就需要重寫自己的HashCode()方法,。下面就舉這樣一個例子: import java.util.*; public class Exp2 { public static void main(String[] args){ HashMap h2=new HashMap(); for(int i=0;i<10;i++) h2.put(new Element(i), new Figureout()); System.out.println("h2:"); System.out.println("Get the result for Element:"); Element test=new Element(5); if(h2.containsKey(test)) System.out.println((Figureout)h2.get(test)); else System.out.println("Not found"); } } class Element{ int number; public Element(int n){ number=n; } } class Figureout{ Random r=new Random(); boolean possible=r.nextDouble()>0.5; public String toString(){ if(possible) return "OK!"; else return "Impossible!"; } } 在這個例子中,,Element用來索引對象Figureout,也即Element為key,F(xiàn)igureout為value,。在Figureout中隨機生成一個浮點數(shù),,如果它比0.5大,打印“OK!”,,否則打印“Impossible!”,。之后查看Element(3)對應的Figureout結(jié)果如何。 結(jié)果卻發(fā)現(xiàn),,無論你運行多少次,,得到的結(jié)果都是“Not found”。也就是說索引Element(3)并不在HashMap中,。這怎么可能呢,? 原因得慢慢來說:Element的HashCode方法繼承自Object,而Object中的HashCode方法返回的HashCode對應于當前的地址,,也就是說對于不同的對象,,即使它們的內(nèi)容完全相同,用HashCode()返回的值也會不同。這樣實際上違背了我們的意圖,。因為我們在使用 HashMap時,,希望利用相同內(nèi)容的對象索引得到相同的目標對象,這就需要HashCode()在此時能夠返回相同的值,。在上面的例子中,,我們期望 new Element(i) (i=5)與 Element test=new Element(5)是相同的,而實際上這是兩個不同的對象,,盡管它們的內(nèi)容相同,,但它們在內(nèi)存中的地址不同。因此很自然的,,上面的程序得不到我們設想的結(jié)果,。下面對Element類更改如下: class Element{ int number; public Element(int n){ number=n; } public int hashCode(){ return number; } public boolean equals(Object o){ return (o instanceof Element) && (number==((Element)o).number); } } 在這里Element覆蓋了Object中的hashCode()和equals()方法。覆蓋hashCode()使其以number的值作為 hashcode返回,,這樣對于相同內(nèi)容的對象來說它們的hashcode也就相同了,。而覆蓋equals()是為了在HashMap判斷兩個key是否相等時使結(jié)果有意義(有關(guān)重寫equals()的內(nèi)容可以參考我的另一篇文章《重新編寫Object類中的方法 》)。修改后的程序運行結(jié)果如下: h2: Get the result for Element: Impossible! 請記?。喝绻阆胗行У氖褂肏ashMap,,你就必須重寫在其的HashCode()。 還有兩條重寫HashCode()的原則: 1. 不必對每個不同的對象都產(chǎn)生一個唯一的hashcode,,只要你的HashCode方法使get()能夠得到put()放進去的內(nèi)容就可以了,。即“不為一原則”。 2. 生成hashcode的算法盡量使hashcode的值分散一些,,不要很多hashcode都集中在一個范圍內(nèi),,這樣有利于提高HashMap的性能。即“分散原則”,。 至于第二條原則的具體原因,,有興趣者可以參考Bruce Eckel的《Thinking in Java》,在那里有對HashMap內(nèi)部實現(xiàn)原理的介紹,,這里就不贅述了,。 掌握了這兩條原則,你就能夠用好HashMap編寫自己的程序了,。不知道大家注意沒有,,java.lang.Object中提供的三個方法: clone(),equals()和hashCode()雖然很典型,,但在很多情況下都不能夠適用,,它們只是簡單的由對象的地址得出結(jié)果。這就需要我們在自己的程序中重寫它們,,其實java類庫中也重寫了千千萬萬個這樣的方法,。利用面向?qū)ο蟮亩鄳B(tài)性——覆蓋,,Java的設計者很優(yōu)雅的構(gòu)建了Java的結(jié)構(gòu),也更加體現(xiàn)了Java是一門純OOP語言的特性,。 Java提供的Collection和Map的功能是十分強大的,,它們能夠使你的程序?qū)崿F(xiàn)方式更為靈活,執(zhí)行效率更高,。希望本文能夠?qū)Υ蠹腋玫氖褂肏ashMap有所幫助,。 |
|
來自: 鬼迷心竅 > 《Java相關(guān)》