《C#高效編程》讀書筆記07-理解GetHashCode()的陷阱


GetHashCode()函數僅會在一個地方用到,即為基於散列(hash)的集合定義的散列鍵時,此類集合包括HashSet 和Dictionary<K,V>容器等。
但object基類提供的GetHashCode()實現有很多問題。

  • 對於引用類型,雖然可以正常工作,但效率很低
  • 對於值類型,基類中的實現有時甚至是不正確的

如果我們定義的類型不會在容器中作為鍵來使用,那就沒有什么問題。但如果創建的類型將被當做散列表中的鍵使用,那么就需要自己實現GetHashCode()。
重載GetHashCode()必須遵循以下規則:

  1. 如果兩個對象相對(由operator==定義),那么它們必須生成相同的散列碼。否則,這樣的散列碼將無法用來查找容器中的對象。
  2. 對於任何一個對象A,A.GetHashCode()必須保持不變。不管在A上調用什么方法,A.GetHashCode()都必然總是返回同一個值。這可以確保放在“桶”中的對象總是位於正確的“桶”中。
  3. 對於所有的輸入,散列函數應該在所有整數中按照隨機分布生成散列碼。這樣散列容器才能得到足夠的效率提升。

Object.GetHashCode()使用System.Object中的一個內部字段來產生序列值。系統創建的每一個對象在創建時都會被指派給一個唯一的對象鍵(一個整數值)。這些鍵從1開始,每創建一個任意類型的新對象,鍵值都會隨之增長。對象標識字段會在System.Object構造函數中設置,並且之后不能更改。對於一個指定的對象,Object.GetHashCode()會返回該值作為散列碼。

但其實Object.GetHashCode()並不滿足第三條規則,一個遞增序列在所有整數范圍內顯然不是一個隨機分布。Object.GetHashCode()返回的散列碼會集中在整數范圍的低端。這就意味着Object.GetHashCode()的實現雖說是正確的,但效率不夠好。
System.VauleType覆寫GetHashCode()方法,為所有值類型提供了一個默認實現。默認的實現會返回類型中定義的第一個字段散列碼。只有當值類型的第一個字段是只讀的情況下,VauleType.GetHashCode()才能正常工作。只有當值類型第一個字段包含的值有着相對隨機分布時,VauleType.GetHashCode()才會產生一個比較高效的散列碼。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM