如果希望某個(gè)類作為HashMap的鍵,則此類必須實(shí)現(xiàn)自己的hashCode和equals方法。

例如有一個(gè)Groundhog類

public class Groundhog{
 protected int number;
 public Groundhog(int n){number = n;}
 public String toString(){
  return "Groundhog #" + number;
 }
}

下面使用Groundhog作為Key,value為一個(gè)字符串

Map map = new HashMap();
map.put(new Groundhog(3), "Ground3");

下面查詢此map:
Groundhog gh = new Groundhog(3);

if(map.containsKey(gh))
{
 System.out.println((String)map.get(gh));
}
else
{
 System.out.println("key not found:" + gh);
}

這段程序代碼看起來很簡(jiǎn)單,但是它無效。因?yàn)镚roundhog繼承基類Object,所以
這里使用Object的hashCode方法生成散列碼,而它默認(rèn)是使用對(duì)象的地址計(jì)算散列碼。
由于兩個(gè)Groundhog(3)生成的散列碼不一樣,所以在Map里面就無法查找。

所以需要實(shí)現(xiàn)hashcode方法,使得相同的對(duì)象具有同樣的hashcode,這里所說的相同的對(duì)象
是從業(yè)務(wù)上來說的,比如業(yè)務(wù)上認(rèn)為number相同,則Groundhog的對(duì)象則相同。

僅僅實(shí)現(xiàn)合適的hashcode方法還是不夠的,hashcode只用于實(shí)現(xiàn)查找hash地址。
還是實(shí)現(xiàn)equals方法,equals方法嚴(yán)格判斷兩個(gè)對(duì)象是否相等。

下面的類Groundhog2就能夠作為Map的key。

public class Groundhog2 extends Groundhog{
 protected int number;
 public Groundhog2(int n){super(n);}
 public int hashCode(){return number;}
 public boolean equals(Object o){
  return (o instanceof Groundhog2)
    && (number == ((Groundhog2)o).number);
 }
}

不僅是HashMap,所有使用散列的數(shù)據(jù)結(jié)構(gòu)(HashSet,HashMap,LinkedHashSet,and
LinkedHashMap)都無法正確的處理你的鍵。當(dāng)然要很好的理解這個(gè)問題,必須了解這些數(shù)據(jù)結(jié)構(gòu)的內(nèi)部構(gòu)造。

首先散列的目的是要使用一個(gè)對(duì)象來查找另一個(gè)對(duì)象。散列就類似于
家里掛的珠簾,一個(gè)個(gè)的珠子構(gòu)成一串,每一串并行而掛。如果把
每一個(gè)珠子看成是Key-value對(duì),串看成是bucket,每一串懸掛的地方定義hash地址
那么這就是一個(gè)HashMap了,只不過珠簾每一串的珠子個(gè)數(shù)是一樣多的,而HashMap通常很難做到
每一個(gè)bucket里面的key-value的個(gè)數(shù)一樣多。HashMap就是根據(jù)key的hashcode計(jì)算其hash code
,hashcode值一樣的key-value對(duì)放在同一個(gè)bucket里面,同一個(gè)bucket里面也就可能好多個(gè)key-value值。
當(dāng)進(jìn)行查找的時(shí)候,HashMap先計(jì)算這個(gè)key的hashcode,通過hashcode馬上定位到這個(gè)key-value在哪個(gè)bucket
里面,這樣查詢的速度就非常快,然后在bucket里面通過equals方法在一個(gè)個(gè)查找,如果有一個(gè)key-value滿足,則就找到了key,value就可以取到了,否則就沒有相應(yīng)的key了。

從上面的對(duì)Hash原理的剖析可以知道,對(duì)于這樣的key必須實(shí)現(xiàn)其hashCode和equals方法,缺一不可。

這里順便提一下HashMap的性能因子,要理解這個(gè)問題必須解釋一些術(shù)語。
容量(Capacity):散列表中bucket的數(shù)量,俗稱桶的數(shù)量
初始化容量(initial capacity):創(chuàng)建散列表時(shí)桶的數(shù)量。HashMap和HashSet都允許在構(gòu)造函數(shù)中指定初始化容量
尺寸(Size):當(dāng)前散列表中記錄的數(shù)量
負(fù)載因子(load factor):等于”尺寸/容量“。負(fù)載因子為0,表示空的散列表,0.5表示半滿的散列表,以此類推。
輕負(fù)載的散列表具有沖突少,適宜插入與查詢的特點(diǎn)。較高的負(fù)載因子雖然會(huì)降低空間的需求,但會(huì)提高查詢的時(shí)間開銷。
如果知道HashMap中會(huì)有很多記錄,在創(chuàng)建時(shí)就使用較大的初始化容量,這樣可以避免自動(dòng)重散列的開銷。