Java集合 3:HashMap,HashTable的實現和區別

一,先說說HashMap

HashMap的數據結構

transient Entry[] table;
static class Entry<K,V> implements Map.Entry<K,V>{
          final K key;
          V  value;
          Entry<K,V> next;
          final int hash;
      .....
//xwxw
}

HashMap存儲函數的實現put(K key,V value):

源碼有點長 就不在這裏打出了 需要的話可以在eclispse中自行查看

根據源碼可以看出,當程序試圖將一個鍵值對放入HashMap中時,首先會計算這個key的hashCode值,然後對這個值進行再哈希,再把rehash過的值和(arr.length-1)進行按位與操作,得到存儲的數組下標,如果該位置無鏈表節點,那就把這個鍵值對放到該位置,但是若該位置有節點,那麼就對鏈表遍歷,看是否有hash,key和要放入節點相同的節點,如果有,就替換掉該節點的Value值,如果沒有相同的,就創建節點放入值,並使用頭插法將該節點插入到鏈表表頭。

主要用到以下幾個方法:

public V put(K key,V value);
void addEntry(int hash,K key,V value,int backetIndex);
void resize(int newCapacity);
void tranfer(Entry[] newTable);
static indexFor(int h,int length);
//xwxw

再哈希的目的是爲了減少哈希衝突(後面會說到),是元素能均勻分佈在數組中,提高存取效率。

問:爲什麼數組長度設置爲2的N次方呢?

     1.因爲上面說到hashmap是通過hash值和(arr.length-1)按位與來得到存儲的數組下標,而若數組的長度都是2的N次方,那麼(arr.length-1)得到的二進制數每個位上的值都爲1,那麼與全部爲1 的一個數進行與操作,速度會很快。

     2.當(arr.length)總是2的N次方時,hash值&(arr.length-1)的操作等價於對數組長度取模,但是&比%的效率更高。

     3.當(arr.length)爲2的N次方時,不同的key算出來的index相同的機率更小,碰撞的機率也就更小。

 

HashMap的度函數的時間get(Object key):

    hashmap的get方法,是先通過key的兩次哈希後的值與(arr.length-1)進行與操作,得到index,然後再對該數組下的鏈表進行遍歷,因爲是在數組中查找是O(1)所以速度是很快的,但是如果hash值相同的元素過多得話,就會造成該鏈表中數據過多,而在鏈表中查找數據又是通過遍歷得到的,是O(n)的,就會影響到查找速度,特別注意:當通過get得到null值時,你無法判斷是沒找到指定元素,還是在hashmap中存在一個value爲null的值,原因是hashmap中允許value的值爲null。

HashMap中的擴容機制:

     當hashmap中節點個數大於arr.length*loadfactor(加載因子)時就會進行擴容,loadfactor的值默認爲0.75,例如默認情況下數組長度爲16,加載因子爲0.75,也就是當節點個數大於16*0.75=12時,就進行擴容,由16擴展爲32,增大一倍。然後再次重新計算每個元素在數組中的位置並放進去,注意!這個操作非常消耗性能。

 

多線程下hash容易出現的問題:

1,多線程下put操作,進行get操作時會出現死鎖的情況,導致cpu滿載,原因是當多線程進行put操作時,如果同時出發了rehash操作,會導致擴容後的hashmap中出現循環節點,再進行get操作,就會死循環。

2.多線程進行put操作可能會導致元素丟失。

二,Hashtable

實現基本與hashmap相同,就不在此贅述了。

三,HashMap和HashTable的區別:

1,hashtable線程安全,方法都是synchronized的,但效率較低。

    hashmap線程不安全,效率稍高,若要在的多線程下使用,需要手動同步,Collections.synchronsizedMap()。

2,hashmap的key和value都可以爲null,hashtable的key和value都不能爲null。

3,hashmap數組的默認大小是16,且一定是2的n次,擴容後值之前的兩倍;

     hashtable的數組默認大小是11,擴容後是之前數組的兩倍+1。

4,hashmap進行再hash作爲hash值並通過hash&length-1來計算數組下標;

      hashtable直接使用對象的hashcode值作爲hash值,並直接與數組長度進行與操作。

5,hashmap中null可以作爲鍵,但是隻能由一個,null也可以作爲值,可以有多個,所以不能用get判斷hashmap中是否存在某個鍵,需要用containsKey()方法來判斷;

    hashtable由於鍵值都不能爲null所以可以用get來判斷。

 

在此附上HashMap的遍歷方式,這是較快的一種 希望大家學習

 Map map = new HashMap();
  Iterator iter = map.entrySet().iterator();
  while (iter.hasNext()) {
  Map.Entry entry = (Map.Entry) iter.next();
  Object key = entry.getKey();
  Object val = entry.getValue();
  }
//xwxw

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章