Hash字典

數(shù)據(jù)結(jié)構(gòu)

凡是Hash字典,首先得有一個(gè)數(shù)組,這個(gè)數(shù)組是用來存儲(chǔ)<Key, Value>對中的Value。
<Key, Value>對中的Key,是用來計(jì)算Value在數(shù)組中的下標(biāo)位置。
一般的做法是,將Key通過特定的Hash算法得到一個(gè)整數(shù)值,然后將這個(gè)整數(shù)值與數(shù)組長度求余,計(jì)算的結(jié)果就是這個(gè)Value在數(shù)組中的位置。
然而當(dāng)出現(xiàn)Hash沖突,也就是不同的Key得到相同的Hash值的時(shí)候,不同的Value將會(huì)保存到數(shù)組中的同一個(gè)位置。那么這個(gè)時(shí)候數(shù)組中就不能單純的只存儲(chǔ)Value了,就要將數(shù)組中的元素鏈表化,變成如下圖所示:


image.png

數(shù)組中的每個(gè)元素,不僅要保存Value,還需要保存Key,以及指向下一個(gè)節(jié)點(diǎn)的指針。
所以當(dāng)出現(xiàn)Hash沖突的時(shí)候,不同的<Key, Value>對都分配到了同一個(gè)數(shù)組下標(biāo)位置,這時(shí)候這些不同的<Key, Value>對組成了一個(gè)鏈表,這個(gè)數(shù)組下標(biāo)下存儲(chǔ)的是一個(gè)鏈表。

Redis中的Hash字典,原理上是類似的,只是在上面包裝了一下,加了一些特有的工程信息。
主要包含三部分:字典,Hash表,Hash表節(jié)點(diǎn)

Hash表

typedef struct dictht {
  dictEntry **table; //指針數(shù)組,用于存儲(chǔ)鍵值對
  unsigned long size; //table數(shù)組的大小
  unsigned long sizemask; //掩碼 = size - 1
  unsigned long used; //table數(shù)組中已存元素個(gè)數(shù)
} dictht

這里的sizemask的用途是,某個(gè)<Kev, Value>對的數(shù)組下標(biāo)值 = Hash(Key) & sizemask。其計(jì)算結(jié)果等同于Hash(Key)%size,但是因?yàn)槭俏贿\(yùn)算,計(jì)算效率要比求余運(yùn)算快得多。

Hash表節(jié)點(diǎn)

Hash表中的元素是用dictEntry來封裝的。

typedef struct dictEntry{
  void *key;
  union {
    void *val;
    uint64_t u64;
    int64_t s64;
    double d;
  } v;
  struct dictEntry *next;
} dictEntry

簡單點(diǎn)理解就是,dictEntry里面包含了key, value,以及指向下一個(gè)dictEntry的next指針。

字典

對hash表進(jìn)行一層封裝,把一些特殊的操作封裝到字典里面,比如計(jì)算hash值,rehash等。

typedef struct dict{
  dictType *type;  //dictType里面封裝了這個(gè)字典需要的一些特殊的操作,比如計(jì)算key的hash值
  void *privatedata;  //傳給dictType里面那些操作所需的參數(shù)數(shù)據(jù)
  dictht ht[2];  //兩個(gè)hash表,大部分情況下只使用ht[0],在擴(kuò)容的情況下需要用到另外一個(gè)
  long rehashidx;  //rehash的進(jìn)度,也就是記錄目前ht[0]這個(gè)table里面哪個(gè)數(shù)組下標(biāo)正在rehash
  unsigned long iterators; //當(dāng)前運(yùn)行的迭代器數(shù)
} dict;

所以Redis Hash字典的整個(gè)結(jié)構(gòu)圖如下:


image.png
  1. 當(dāng)往Hash字典中加入<Key, Value>對時(shí),首先在Dict的type中根據(jù)Key計(jì)算出Hash值;
  2. 然后獲取dictht[0].sizemask,通過Hash(key)&dictht[0].sizemask,得到這個(gè)<Key, Value>對在dictht[0].table數(shù)組中的位置。
  3. 將傳入的<Key, Value>封裝成一個(gè)新的dictEntry存入計(jì)算出來的位置。

擴(kuò)容和縮容

當(dāng)持續(xù)往字典里面添加<Key, Value>對后,字典空間慢慢會(huì)不夠,這個(gè)時(shí)候就需要擴(kuò)容。

  1. 申請一塊新空間,大小為原來的兩倍,并且往上取2的N次方。比如原來字典里面的數(shù)組長度是3,擴(kuò)容的時(shí)候要申請大小為8的空間,因?yàn)?的兩倍是6,6不是2的N次方,所以往上到8;
  2. 將字典的rehashidx設(shè)置為0. 并將ht[1]指向這塊擴(kuò)容后的新空間, ht[1]的size, sizemask, used都相應(yīng)的更新;
  3. 所有的插入操作都rehash到ht[1]。查找,刪除,更新則兼顧ht[0]和ht[1];
  4. 最后把ht[0]里面老的元素都rehash到ht[1],rehashidx設(shè)置為正在rehash的ht[0]里面元素的下標(biāo)值;
  5. 刪除ht[0]里面的元素,對調(diào)ht[0]和ht[1],將rehashidx設(shè)置為-1;
    縮容也是類似的流程。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 上一篇文章詳細(xì)介紹了Redis的五種常用數(shù)據(jù)類型,每種數(shù)據(jù)類型在內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)都會(huì)因情況而異。接下來,我會(huì)用幾篇...
    Javar閱讀 2,061評論 0 2
  • 哈希表在C++中對應(yīng)的是map數(shù)據(jù)結(jié)構(gòu),但在Redis中稱作dict(字典)。Redis只是用了幾個(gè)簡單的結(jié)構(gòu)體和...
    linux大本營閱讀 785評論 0 0
  • 1、字典,又稱符號(hào)表(symbol table)、關(guān)聯(lián)數(shù)組(associative array)、或者映射(map...
    大飛飛_s8閱讀 429評論 2 0
  • Redis 之字典和跳表 字典 Redis整個(gè)數(shù)據(jù)庫其實(shí)就是一個(gè)大的字典 以上命令實(shí)際上就是設(shè)置了數(shù)據(jù)庫字典中一個(gè)...
    lxfeng閱讀 1,166評論 0 0
  • 一、概念 字典, 又稱符號(hào)表(symbol table)、關(guān)聯(lián)數(shù)組(associative array)或者映射(...
    Vic_is_new_Here閱讀 560評論 0 0

友情鏈接更多精彩內(nèi)容