hash table 也叫做時 “散列表”、哈希表
redis的數據結構也有用到這個數據結構。哈希表用的時數組支持下標隨機訪問數據的特性,所以哈希表其實就是數組得一種擴展,是由數組演化而來的。
通過hash函數得到的hash值有一下幾個特點:
1、hash函數得到的 value值 是一個非負整數
2、如果key相同 通過hash函數得到的 value值肯定相同
3、如果key不相同的話,通過hash函數得到的value不一定不同(涉及到了哈希沖突問題)
相對於哈希沖突而言,業界比較著名的哈希算法函數 MD5、SHA、CRC 都不發完全避免哈希沖突的問題。數組得存儲空間有限,也會加大哈希沖突的概率。
以下為解決hash沖突問題的方法:
1、開放尋址法
開發尋址法的核心思想,hash(key)得到的hash值,在哈希表中找到下標值,發現已經被占用了,就會從存儲位置下一個開始進行探測尋找,尋找沒有被占用的空閑位置,
找到了位置以后將數據存入。如果發現找到尾部以后都被占用,則會從頭開始繼續尋址,直到找到為止。數據量大的話,性能會就下降
查找操作:
通過hash(key) 查找該hash值對應的數據時,會通過hash函數得到一個hash值,然后比較數組種下標為散列值得數據和要查找的元素是否相等,不相等則順序往后查找,
如果遍歷到空閑位置還沒有找到的話就返回為 未找到。
刪除操作:
在hash表中是否可直接刪除一個數據,然后將hash表中直接置為空呢?!其實時不可以的,因為在查找數據的過程中,遇到空閑位置時,則認為該數據不存在hash表中,所以如果在hash表中刪除一個
一個數據時將位置 設置為空,會造成查找數據出現問題。解決方案就是將刪除的數據置位deleted ,這樣在查找數據時就會跳過繼續查找下一個位置。
2、鏈表法
hash表有一個指標來表示hash沖突嚴重程度:
hash表裝載因子 = 填入表中的元素個數 / hash表長度;
"hash表裝載因子" 這個值越大表明hash沖突越嚴重,hash表的性能就會下降的。
下面一個比較常用的解決hash沖突的辦法就是 鏈表法 。在hash表有一個概念叫做hash槽,每個hash槽會對應一個鏈表,所有的通過hash函數得到的散列值相同的元素會放在相同的hash槽位對應的鏈表中。
插入數據:
通過hash函數查詢到對應的hash槽位,將數據插入到對應鏈表的中。這個插入數據的時間復雜度時O(1)
查找、刪除數據:
查找數據、刪除數據時,通過hash函數得到的哈希值,找到對應的哈希槽位,遍歷槽位對應的鏈表,找到要刪除或查找的數據進行對應的操作。
這個時間復雜度取決於hash槽位對應的鏈表的長度。
比如:元素個數為n個
hash槽位有m個
那時間復雜度就是 O(n/m)
3、hash槽動態擴容法--rehash
當裝載因子過大時,會自動啟動擴容,需要重新申請內存空間,重新計划哈希值位置,並且需要搬移數據。
裝載因子設置一個閾值,當大於閾值時啟動擴容,這個閾值設置需要權衡好,太大容易造成哈希沖突嚴重,太小容易造成內存空間的浪費,申請的內存大小時原來的2倍。
假如一個數據量很大的數據,當達到設置的閾值時,需要進行數據搬移,但是數據太大了,一次性搬移會很耗時也會造成阻塞,所以在需要插入新數據時就會把數據插入到hash表中,並且從從舊的hash表中遷移一部分數據到新的hash表中,一直重復,直到舊的hash數據全部遷移到新的hash中,這樣就不會一次性遷移了,插入的數據的速度會更快。
查詢數據過程中,如果處於時新舊hash表遷移數據,需要先查詢舊的hash表,沒有查到再次查詢新的hash表