GetHashCode()函數僅會在一個地方用到,即為基於散列(hash)
的集合定義的散列鍵時,此類集合包括HashSet
但object基類提供的GetHashCode()實現有很多問題。
- 對於引用類型,雖然可以正常工作,但效率很低
- 對於值類型,基類中的實現有時甚至是不正確的
如果我們定義的類型不會在容器中作為鍵來使用,那就沒有什么問題。但如果創建的類型將被當做散列表中的鍵使用,那么就需要自己實現GetHashCode()。
重載GetHashCode()必須遵循以下規則:
- 如果兩個對象相對(由operator==定義),那么它們必須生成相同的散列碼。否則,這樣的散列碼將無法用來查找容器中的對象。
- 對於任何一個對象A,A.GetHashCode()必須保持不變。不管在A上調用什么方法,A.GetHashCode()都必然總是返回同一個值。這可以確保放在“桶”中的對象總是位於正確的“桶”中。
- 對於所有的輸入,散列函數應該在所有整數中按照隨機分布生成散列碼。這樣散列容器才能得到足夠的效率提升。
Object.GetHashCode()使用System.Object中的一個內部字段來產生序列值。系統創建的每一個對象在創建時都會被指派給一個唯一的對象鍵(一個整數值)。這些鍵從1開始,每創建一個任意類型的新對象,鍵值都會隨之增長。對象標識字段會在System.Object構造函數中設置,並且之后不能更改。對於一個指定的對象,Object.GetHashCode()會返回該值作為散列碼。
但其實Object.GetHashCode()並不滿足第三條規則,一個遞增序列在所有整數范圍內顯然不是一個隨機分布。Object.GetHashCode()返回的散列碼會集中在整數范圍的低端。這就意味着Object.GetHashCode()的實現雖說是正確的,但效率不夠好。
System.VauleType覆寫GetHashCode()方法,為所有值類型提供了一個默認實現。默認的實現會返回類型中定義的第一個字段散列碼。只有當值類型的第一個字段是只讀的情況下,VauleType.GetHashCode()才能正常工作。只有當值類型第一個字段包含的值有着相對隨機分布時,VauleType.GetHashCode()才會產生一個比較高效的散列碼。