原理node
咱們都知道怎麼使用goLang中的map來存儲鍵值對類型的數據,可是它的內部實現是怎麼樣的?算法
其實map是一種HashMap,表面上看它只有鍵值對結構,實際上在存儲鍵值對的過程當中涉及到了數組和鏈表。HashMap之因此高效,是由於其結合了順序存儲(數組)和鏈式存儲(鏈表)兩種存儲結構。數組是HashMap的主幹,在數組下有有一個類型爲鏈表的元素。數組
這是一個簡單的HashMap的結構圖:性能優化
HashMap結構架構
當咱們存儲一個鍵值對時,HashMap會首先經過一個哈希函數將key轉換爲數組下標,真正的key-value是存儲在該數組對應的鏈表裏。併發
HashMap的數組每每是有限的,那當要存儲的鍵值對不少數組不夠或者兩個鍵值對哈希運算後的值相同時,不就會有不一樣的鍵值對存儲在同一個數組下嗎?是的,這個就叫作哈希碰撞。當發生哈希碰撞時,鍵值對就會存儲在該數組對應鏈表的下一個節點上。分佈式
儘管這樣,HashMap的操做效率也是很高的。當不存在哈希碰撞時查找複雜度爲O(1),存在哈希碰撞時複雜度爲O(N)。因此,但從性能上講HashMap中的鏈表出現越少,性能越好;固然,當存儲的鍵值對很是多時,從存儲的角度鏈表又能分擔必定的壓力。函數
代碼實現微服務
KVMap高併發
首先,HashMap存儲的是鍵值對,因此須要一個鍵值對類型。
//鏈表結構裏數據的數據類型 鍵值對typeKVstruct{ KeystringValuestring}
LinkNode
鍵值對又是主要存儲在鏈表裏的,因此須要一個鏈表類。
//鏈表結構typeLinkNodestruct{//節點數據Data KV//下一個節點NextNode *LinkNode}//建立只有頭結點的鏈表funcCreateLink()*LinkNode{//頭結點數據爲空 是爲了標識這個鏈表尚未存儲鍵值對varlinkNode = &LinkNode{KV{"",""},nil}returnlinkNode}
當發生哈希碰撞時,鍵值對會存儲在新建的鏈表節點上。這裏須要一個添加節點的功能,咱們這裏採用尾插法添加節點。
//尾插法添加節點,返回鏈表總長度func(link *LinkNode)AddNode(dataKV) int {varcount=0//找到當前鏈表尾節點tail := linkfor{count+=1iftail.NextNode==nil{break}else{ tail = tail.NextNode} }varnewNode = &LinkNode{data,nil} tail.NextNode= newNodereturncount+1}
HashMap
接下來,就是豬腳HashMap登場了。
//HashMap木桶(數組)的個數constBucketCount =16typeHashMapstruct{//HashMap木桶Buckets [BucketCount]*LinkNode}//建立HashMapfuncCreateHashMap()*HashMap{ myMap := &HashMap{}//爲每一個元素添加一個鏈表對象fori :=0; i < BucketCount ; i++ { myMap.Buckets[i] = CreateLink() }returnmyMap}
咱們須要一個哈希散列算法,將key轉化爲一個0-BucketCount的整數,做爲存放它的數組的下標。這裏這個散列算法,應儘量隨機地使新增的鍵值對均勻地分佈在每一個數組下。
通常像go的map和Java的HashMap都會有一個複雜的散列算法來達到這個目的,咱們這裏只是爲了講HashMap原理,暫且就用一個簡單的方法來求出下標。
//自定義一個簡單的散列算法,它能夠將不一樣長度的key散列成0-BucketCount的整數funcHashCode(keystring)int{varsum =0fori :=0; i
往HashMap裏添加鍵值對在這裏順便給你們推薦一個架構交流羣:617434785,裏面會分享一些資深架構師錄製的視頻錄像:有Spring,MyBatis,Netty源碼分析,高併發、高性能、分佈式、微服務架構的原理,JVM性能優化這些成爲架構師必備的知識體系。還能領取免費的學習資源。相信對於已經工做和遇到技術瓶頸的碼友,在這個羣裏會有你須要的內容。
//添加鍵值對func(myMap *HashMap)AddKeyValue(keystring, valuestring){//1.將key散列成0-BucketCount的整數做爲Map的數組下標varmapIndex = HashCode(key)//2.獲取對應數組頭結點varlink = myMap.Buckets[mapIndex]//3.在此鏈表添加結點iflink.Data.Key ==""&& link.NextNode ==nil{//若是當前鏈表只有一個節點,說明以前未有值插入 修改第一個節點的值 即未發生哈希碰撞link.Data.Key = key link.Data.Value = value fmt.Printf("node key:%v add to buckets %d first node\n", key, mapIndex) }else{//發生哈希碰撞index := link.AddNode(KV{key, value}) fmt.Printf("node key:%v add to buckets %d %dth node\n", key, mapIndex, index) }}
根據鍵從HashMap裏取出對應的值
//按鍵取值func(myMap *HashMap)GetValueForKey(keystring)string{//1.將key散列成0-BucketCount的整數做爲Map的數組下標varmapIndex = HashCode(key)//2.獲取對應數組頭結點varlink = myMap.Buckets[mapIndex]varvaluestring//遍歷找到key對應的節點head := linkfor{ifhead.Data.Key == key { value = head.Data.Valuebreak}else{ head = head.NextNode } }returnvalue}
Main_test
packagemainimport("chaors.com/LearnGo/BlockchainCryptography/HashMap")funcmain(){ myMap := HashMap.CreateHashMap() myMap.AddKeyValue("001","1") myMap.AddKeyValue("002","2") myMap.AddKeyValue("003","3") myMap.AddKeyValue("004","4") myMap.AddKeyValue("005","5") myMap.AddKeyValue("006","6") myMap.AddKeyValue("007","7") myMap.AddKeyValue("008","8") myMap.AddKeyValue("009","9") myMap.AddKeyValue("010","10") myMap.AddKeyValue("011","11") myMap.AddKeyValue("012","12") myMap.AddKeyValue("013","13") myMap.AddKeyValue("012","14") myMap.AddKeyValue("015","15")}
RUN
main_test.png
一個簡單的HashMap就實現了,雖然咱們的散列算法只是用了一個簡單的轉換算法,這對咱們理解HashMap原理已經足夠了。