利用哈希的其中一個思想,相同的對象的哈希值相同,可以用來提升一些大對象集合的進行對象相等判斷的性能。大對象的相等判斷指的是有某些類型的相等判斷需要用到對象的很多屬性或字段進行參與判斷邏輯才能判斷兩個對象是否相等,當這些大對象存放在集合里面,此時進行大量的相等判斷將會因為需要有大量的屬性或字段的判斷而降低性能。本文告訴大家如何使用此哈希的思想提升判斷的性能
故事的背景是我在做一個比 Office 的 Word 差得多的軟件,此軟件有文本的功能,允許每個文字都有自己的文本屬性。有趣的是,我期望將文本的文本屬性進行合並,總不能一篇一萬字的博客有一萬個文本屬性吧。但文本屬性是一個比較大的類型,里面包含了一堆屬性,如字體字號等等
在拿到輸入的一堆文本屬性的集合里面,需要進行文本屬性對象之間的相等判斷用於合並多余的文本屬性。在使用 dotTrace 進行性能測量時,了解到有大量的資源都用在了相等判斷里面,因為一個文本屬性和另一個文本屬性的相等比較大約需要比較近 100 個屬性。不要聽着 100 個屬性很驚訝,在 Word 里面可是按照 MB 計算的屬性量哦
在進行性能優化的時候,我考慮用上哈希的思想。思想就是將大對象的相等比較分為兩步,第一步判斷大對象的哈希值是否相等。基於相等的對象的哈希值相等的思想,可以了解到想要兩個對象相等,第一步判斷哈希值必須相等。在判斷哈希之后再進行大對象原本的對象相等判斷
判斷哈希值相當於只是判斷一個 int 值而已,占用資源基本可以被忽略。因此可以在存在比較多不相同的對象的時候,可以提升對不相同對象的判斷的性能從而提升集合的判斷相等的性能
以下是更詳細的細節
在制作對象的哈希值的時候,期望是將所有參與相等判斷的屬性和字段都加入到哈希值的創建中,這樣創建出來的哈希值將會包含所有參與判斷的字段和屬性的信息。但同時也會帶來一個問題是哈希值的計算也是需要計算資源的,可以考慮將哈希值進行緩存
在 dotnet 里面,默認對象的 GetHashCode 是不推薦將非只讀的屬性和字段加入到此方法的哈希值制作。但是在本文的需求下,是無視此條例,需要將所有參與判斷相等的屬性和字段都加入哈希值的制作。因此建議本文的方法不要放入到對象的 GetHashCode 方法里面,而是自己再創建一個新方法或者放在某個輔助類
在制作大對象的哈希值時,需要充分考慮業務上的需要,寧可哈希沖突大一些也不要誤判。也就是說寧可兩個不相等的對象返回相同的哈希值,也不要有存在某些相等的對象可能返回不同的哈希值。其細節在於,在大對象里面,引用的一些屬性對應的類型所獲取的哈希值如果不能准確獲取,如是一個接口等,那么建議將此屬性或字段不加入到哈希值的制作。相當於判斷哈希值是例外了此屬性或字段的判斷。其原因是接口相對來說是自由的,如果有某些業務詭異實現了此接口,讓原本兩個相等的對象返回了不相等的哈希值,那么將會讓本文的邏輯炸掉