map的操做和約束

Go語言的字典類型其實就是一個哈希表的特定實現。字典的鍵的類型是受限的,元素的類型能夠是任意類型。算法

字典的鍵爲何受限呢?

典的鍵-元素對的增刪改查的操做,就是哈希表的映射過程。數組

以查找爲例:安全

  1. 在哈希表中查找與某個鍵值對應的元素值的時候,須要先把鍵值做爲參數傳給這個哈希表。哈希表用哈希函數把鍵值轉換爲哈希值。

哈希值一般是一個無符號的整數。一個哈希表會持有必定數量的哈希桶。併發

  1. 用這個鍵的哈希值的低幾位去定位到一個哈希桶,而後再去這個哈希桶中查找這個鍵。

因爲鍵-元素對老是被捆綁在一塊兒存儲的,因此一旦找到了鍵,就必定能找到對應的元素值。函數

  1. 應的元素值以後,就會將元素值做爲結果返回。

咱們已經知道了,映射的第一步是把鍵值轉換成哈希值。那麼字典的鍵不能死哪些類型呢?

回答:字典的鍵不能是函數類型,字典類型,切片類型。性能

那麼,剩下的:基本數據類型,(int系列,float系列,string,複數),數組,結構體,指針,接口等均可以做爲鍵類型。ui

問題解析

Go語言規範規定:在鍵類型的值之間必須能夠施加操做符==!=。也就是說鍵類型的值必需要支持判等操做。指針

注意:

  1. 鍵類型是接口類型時,鍵值的實際類型也不能是上述三種類型。不然會在程序運行時引起panic。
  2. 最好不要把字典的鍵類型設定爲任何接口類型。
  3. 若是鍵類型是數組類型,要確保該類型的元素類型不是函數類型,字典類型或切片類型
  4. 若是鍵類型是結構體類型,也要保證其中字段的類型的合法性。不合法的類型被埋藏的多深,都會被Go語言編譯器揪出來的。

引伸:優先考慮哪些類型做爲字典的鍵類型?

從性能角度

求哈希和判等的速度越快,對應的類型就越適合做爲鍵類型。code

寬度越小的類型求哈希的速度一般越快。好比布爾類型,整數類型,浮點數類型,複數類型和指針類型。接口

對於字符串類型,因爲它的寬度是不定的,因此要看它的值的具體長度,長度越短,求哈希越快

類型的寬度是指它的單個值須要佔用的字節數。好比,bool,int8,uint8類型的一個值須要佔用的字節都是1個。所以寬度就都是1。

對於高級類型:

數組:對數組求哈希其實是依次求得它的每一個元素的哈希值並進行合併,因此速度就取決於它的元素類型以及它的長度。

結構體:對結構體類型的值求哈希值實際上就是對它的全部字段求哈希值並進行合併。因此關鍵在於它的各個字段的類型和字段的數量。

接口:對於接口類型,具體的哈希算法由值的實際類型決定。

結論

不建議使用高級數據類型做爲字典的鍵類型。優先選用數值類型和指針類型。一般狀況下類型的寬度越小越好。若是非要選擇字符串類型的話,最好對鍵值的長度進行額外的約束。

一個是由於對他們求哈希值和判等的速度較慢,還由於他們的值中存在變數。好比,改變數組中任意一個元素,數組的哈希值就變了。

雖然結構體能夠經過控制其中字段的訪問權限,來防止外界修改它。

把接口類型做爲鍵類型最危險。

在值爲nil的字典上執行讀操做會成功嗎?那寫操做呢?

因爲字典是引用類型,當咱們僅僅聲明而不初始化一個字典類型的變量的時候,這個變量的值就是nil。

對這個值爲nil的字典進行添加鍵-元素對,會引起錯誤。其餘操做沒有問題。

字典類型的值是併發安全的嗎?

非原子操做須要加鎖,map操做不是併發安全的,map併發讀寫須要加鎖。

判斷一個操做是不是原子的,可使用go run race命令作數據的競爭檢測。經過 sync.Map 或本身使用sync.RWMutex本身實現併發互斥邏輯

相關文章
相關標籤/搜索