hashmap數據結構

什麼是hashcode 
    分析HashMap以前先介紹下什麼Hashcode(散列碼)。它是一個int,每一個對象都會有一個hashcode,它在內存的存放位置是放在對象的頭部(對象頭部存放的信息有hashcode,指向Class的引用,和一些有關垃圾回收信息)。具體如何生成hashcode,這個至關複雜,因爲咱們的主題是「淺析」,因此不深刻探討。有個問題須要講的是,若是在你的類中覆蓋了Object的equals(Object)方法,那麼你必須覆蓋hashCode方法,否則,當你使用HashMap,HashSet,HashTable時會出現問題。具體緣由下文會詳細描述。 
String類的hashcode 
    String類重寫了Object類中的equals和hashCode方法,緣由很簡單,Object中的equals方法是指比較兩個對象是否是指向同一個引用對象,而String類指須要比較內容相不相等就能夠了。因此String覆蓋了equals方法,同時覆蓋了hashCode方法。這裏須要提一下Object規範裏規定:若是兩個對象根據equals(Object)方式是相等的,那麼這兩個對象的hashCode必定要相等。 
String中的hashcode算法很簡單以下: 
Java代碼   
  1. for (int i = 0; i < len; i++) {  
  2.                 h = 31*h + val[off++];  
  3.             }  

Val是一個char[],存放的是字符串的各個字符;Len是字符串長度;off從0開始;h從0開始。好比一個字符串「abc」(a的ascii碼是97),它的hashcode算法是: 
h = 31 * 0 + 97 ==> h = 97; 
h = 31 * 97 + 98 ==> h = 3105; 
h = 31 * 3105 + 99 ==> h = 96354; 
因此「abc」的hashCode就是96354 
存儲方式 
    列表的存儲方式是基於數組,以下圖: 
 

    鏈表的存儲方式是基於指針,以下圖:(單向鏈表)  

    HashMap的存儲方式是上面兩種的結合,以下圖: 
 

HashMap的存取    HashMap的功能是經過「鍵(key)」可以快速的找到「值」。下面咱們分析下HashMap存數據的基本流程:    一、 當調用put(key,value)時,首先獲取key的hashcode,int hash = key.hashCode();    二、 再把hash經過一下運算獲得一個int h.hash ^= (hash >>> 20) ^ (hash >>> 12);int h = hash ^ (hash >>> 7) ^ (hash >>> 4);爲何要通過這樣的運算呢?這就是HashMap的高明之處。先看個例子,一個十進制數32768(二進制1000 0000 0000 0000),通過上述公式運算以後的結果是35080(二進制1000 1001 0000 1000)。看出來了嗎?或許這樣還看不出什麼,再舉個數字61440(二進制1111 0000 0000 0000),運算結果是65263(二進制1111 1110 1110 1111),如今應該很明顯了,它的目的是讓「1」變的均勻一點,散列的本意就是要儘可能均勻分佈。那這樣有什麼意義呢?看第3步。    三、 獲得h以後,把h與HashMap的承載量(HashMap的默認承載量length是16,能夠自動變長。在構造HashMap的時候也能夠指定一個長度。這個承載量就是上圖所描述的數組的長度。)進行邏輯與運算,即 h & (length-1),這樣獲得的結果就是一個比length小的正數,咱們把這個值叫作index。其實這個index就是索引將要插入的值在數組中的位置。第2步那個算法的意義就是但願可以得出均勻的index,這是HashTable的改進,HashTable中的算法只是把key的hashcode與length相除取餘,即hash % length,這樣有可能會形成index分佈不均勻。還有一點須要說明,HashMap的鍵能夠爲null,它的值是放在數組的第一個位置。    四、 咱們用table[index]表示已經找到的元素須要存儲的位置。先判斷該位置上有沒有元素(這個元素是HashMap內部定義的一個類Entity,基本結構它包含三個類,key,value和指向下一個Entity的next),沒有的話就建立一個Entity<K,V>對象,在table[index]位置上插入,這樣插入結束;若是有的話,經過鏈表的遍歷方式去逐個遍歷,看看有沒有已經存在的key,有的話用新的value替換老的value;若是沒有,則在table[index]插入該Entity,把原來在table[index]位置上的Entity賦值給新的Entity的next,這樣插入結束。再回頭看看前面提到的爲何覆蓋了equals方法以後必定要覆蓋hashCode方法,很簡單,好比,String a = new String(「abc」);String b = new String(「abc」);若是不覆蓋hashCode的話,那麼a和b的hashCode就會不一樣,把這兩個類當作key存到HashMap中的話就會出現問題,就會和key的惟一性相矛盾。    HashMap取的過程也相似。太累了,不寫了。趕忙結束。    文中可能會有錯誤的地方請指出,謝謝!
相關文章
相關標籤/搜索