如果希望某個類作為HashMap的鍵,則此類必須實現自己的hashCode和equals方法。

例如有一個Groundhog類

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

下面使用Groundhog作為Key,value為一個字符串

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);
}

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

所以需要實現hashcode方法,使得相同的對象具有同樣的hashcode,這里所說的相同的對象
是從業務上來說的,比如業務上認為number相同,則Groundhog的對象則相同。

僅僅實現合適的hashcode方法還是不夠的,hashcode只用于實現查找hash地址。
還是實現equals方法,equals方法嚴格判斷兩個對象是否相等。

下面的類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,所有使用散列的數據結構(HashSet,HashMap,LinkedHashSet,and
LinkedHashMap)都無法正確的處理你的鍵。當然要很好的理解這個問題,必須了解這些數據結構的內部構造。

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

從上面的對Hash原理的剖析可以知道,對于這樣的key必須實現其hashCode和equals方法,缺一不可。

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