糾正<存儲 dict 的元素前是計算 key 的 hash 值?>

前幾天發了一篇名爲 存儲 dict 的元素前是計算 key 的 hash 值? 的文章,因缺少相關的背景知識,致使得出了不正確的推論。
那篇文章的推論是python

在不考慮 hash 衝突的狀況下, 'a' 所在內存地址的 hash 值與 'b' 所在內存地址的 hash 值之間的差值 和 'a' 的內存地址與 'b' 的內存地址之間的差值 相等,也就是說如下的等式成立纔對
hash(id('a')) - hash(id('b')) == id('a') - id('b')

簡單說是:存儲 dict 的元素前計算的是 key 所在內存地址的 hash 值
上面的等式是成立的算法

>>> hash(id('a')) - hash(id('b')) == id('a') - id('b')
True
>>> id('a') - id('b')
1680
>>> hash(id('a')) - hash(id('b'))
1680

可是須要糾正說明的是,我上面的那個推論是錯的!數組

等式成立的緣由

這裏先說上面的等式爲何成立,由於整數的 hash 值是其自己數據結構

>>> a
1234567
>>> hash(a)
1234567

又由於內存地址是個整數,因此內存地址的 hash 值也是其自己,即和內存地址同樣的值app

>>> my_dict = {'a': 'apple', 'b': 'banana'}
>>> hash(id('a')) == id('a')
True
>>> id('a')
2673717403464
>>> hash(id('a'))
2673717403464

這就是爲何這個等式成立函數

hash(id('a')) - hash(id('b')) == id('a') - id('b')

推論錯誤的緣由

若是兩個值不一樣,那麼它們的內存地址也不一樣,這是正確的,但我錯誤的認爲若是值相同,那麼內存地址也相同。下面是一個具備相同值不一樣內存地址的例子3d

>>> c = 'a-c'
>>> d = 'a-c'
>>> id(c) == id(d)
False
>>> id(c)
2673720167592
>>> id(d)
2673720167704
注:上面的 cd 雖然值是相同的,但它們不是同一個對象,因此內存地址不同

回到那個錯誤的推論:code

存儲 dict 的元素前計算的是 key 所在內存地址的 hash 值

該推論成立的前提之一是:相同的 key 值的內存地址必須相同,但事實是像上面的例子同樣,相同的 key 值能夠擁有不一樣的內存地址對象

假設該推論成立的話,就會致使 dict 中出現兩個相同的 key 值,但事實不是這樣的,即使內存地址不一樣,只要值相同就不能夠同時做爲 dict 的 key,後者會覆蓋前者。索引

>>> c
'a-c'
>>> d
'a-c'
>>> {c: 0, d: 1}
{'a-c': 1}

由於相同的 key 具備相同的 hash 值

>>> hash(c) == hash(d)
True
>>> hash(c)
-8124728931706162487
>>> hash(d)
-8124728931706162487

存儲 dict 的元素前是計算 key 的 hash 值

首先了解下關於 key 與其 hash 值之間的幾點事實:

  1. 相同的 key 確定具備相同的 hash 值;

    應該不用解釋,這是 hash 算法決定的;
  2. 不一樣的 key 也可能具備相同的 hash 值;

    由於 hash 算法會將任何長度的數據轉換成一個固定長度的字符串(int 對象除外),因此可能生成的 hash 值的數量是有限的,而可用來計算 hash 值的數據量理論上是無窮的,這就形成兩個數據的 hash 值可能相同
  3. 具備相同 hash 值的 key 不必定相同;

    緣由正是由於不一樣的 key 能夠具備相同的 hash 值
  4. 具備不一樣 hash 值的 key 確定不一樣

    緣由正是由於相同的 key 具備相同的 hash 值;

dict 是基於哈希表的數據結構,哈希表是一塊連續的內存空間(數組),由於所存儲的 key-value 散落在不一樣的位置上,key-value 之間存在大量的空白空間是很常見的,因此哈希表又稱散列表。
dict 本質就是一個能利用散列函數將 key 轉化爲索引的數組(散列函數+數組),散列函數的功能之一是將 key 值轉換爲數組索引(dict 的 key 具備惟一性的本質是數組索引的惟一性)。在轉換的過程當中,須要對 key 進行 hash 值計算,計算 hash 值的目的是爲了肯定 key 應該存儲在數組中的哪一個位置(索引),即定位,而不是判斷兩個 key 是否相同。由於經過比較 hash 值是沒法判斷兩個 key 是否相同的(參考前面的第 3 點事實),因此當 hash 值相同時,會定位到相同的表元(索引對應的元素),該表元裏的 key 是否與計算的 key 相等還須要進一步判斷。

反正存儲 dict 的元素前仍是計算 key 的 hash 值,但這只是散列函數中的其中一個過程或步驟。

閱讀更多

相關文章
相關標籤/搜索