天天看點

一次性搞清楚equals和hashCode前言 equals編寫指導public int hashCode() hashCode編寫指導

前言

在程式設計中,有很多的“公約”,遵守約定去實作你的代碼,會讓你避開很多坑,這些公約是前人總結出來的設計規範。

Object類是Java中的萬類之祖,其中,equals和hashCode是2個非常重要的方法。

這2個方法總是被人放在一起讨論。最近在看集合架構,為了打基礎,就決定把一些細枝末節清理掉。一次性搞清楚!

下面開始剖析。

Object類中預設的實作方式是 : return this == obj 。那就是說,隻有this 和 obj引用同一個對象,才會傳回true。

而我們往往需要用equals來判斷 2個對象是否等價,而非驗證他們的唯一性。這樣我們在實作自己的類時,就要重寫equals.

按照約定,equals要滿足以下規則:

自反性: x.equals(x) 一定是true

對null: x.equals(null) 一定是false

對稱性: x.equals(y) 和 y.equals(x)結果一緻

傳遞性: a 和 b equals , b 和 c equals,那麼 a 和 c也一定equals。

一緻性: 在某個運作時期間,2個對象的狀态的改變不會不影響equals的決策結果,那麼,在這個運作時期間,無論調用多少次equals,都傳回相同的結果。

一個例子

equals編寫指導

Test類對象有2個字段,num和data,這2個字段代表了對象的狀态,他們也用在equals方法中作為評判的依據。

在第8行,傳入的比較對象的引用和this做比較,這樣做是為了 save time ,節約執行時間,如果this 和 obj是 對同一個堆對象的引用,那麼,他們一定是qeuals 的。

接着,判斷obj是不是為null,如果為null,一定不equals,因為既然目前對象this能調用equals方法,那麼它一定不是null,非null 和 null當然不等價。

然後,比較2個對象的運作時類,是否為同一個類。不是同一個類,則不equals。getClass傳回的是 this 和obj的運作時類的引用。如果他們屬于同一個類,則傳回的是同一個運作時類的引用。注意,一個類也是一個對象。

1、有些程式員使用下面的第二種寫法替代第一種比較運作時類的寫法。應該避免這樣做。

它違反了公約中的對稱原則。

2、按照第一種方法實作,那麼equals隻能比較同一個類的對象,不同類對象永遠是false。但這并不是強制要求的。一般我們也很少需要在不同的類之間使用equals。

3、在具體比較對象的字段的時候,對于基本值類型的字段,直接用 == 來比較(注意浮點數的比較,這是一個坑)對于引用類型的字段,你可以調用他們的equals,當然,你也需要處理字段為null 的情況。對于浮點數的比較,我在看Arrays.binarySearch的源代碼時,發現了如下對于浮點數的比較的技巧:

4、并不總是要将對象的所有字段來作為equals 的評判依據,那取決于你的業務要求。比如你要做一個家電功率統計系統,如果2個家電的功率一樣,那就有足夠的依據認為這2個家電對象等價了,至少在你這個業務邏輯背景下是等價的,并不關心他們的價錢啊,品牌啊,大小等其他參數。

5、最後需要注意的是,equals 方法的參數類型是Object,不要寫錯!

public int hashCode()

這個方法傳回對象的散列碼,傳回值是int類型的散列碼。

對象的散列碼是為了更好的支援基于哈希機制的Java集合類,例如 Hashtable, HashMap, HashSet 等。

關于hashCode方法,一緻的約定是:

重寫了euqls方法的對象必須同時重寫hashCode()方法。

如果2個對象通過equals調用後傳回是true,那麼這個2個對象的hashCode方法也必須傳回同樣的int型散列碼

如果2個對象通過equals傳回false,他們的hashCode傳回的值允許相同。(然而,程式員必須意識到,hashCode傳回獨一無二的散列碼,會讓存儲這個對象的hashtables更好地工作。)

在上面的例子中,Test類對象有2個字段,num和data,這2個字段代表了對象的狀态,他們也用在equals方法中作為評判的依據。那麼, 在hashCode方法中,這2個字段也要參與hash值的運算,作為hash運算的中間參數。這點很關鍵,這是為了遵守:2個對象equals,那麼 hashCode一定相同規則。

也是說,參與equals函數的字段,也必須都參與hashCode 的計算。

相比 于 equals公認實作約定,hashCode的公約要求是很容易了解的。有2個重點是hashCode方法必須遵守的。約定的第3點,其實就是第2點的細化,下面我們就來看看對hashCode方法的一緻約定要求。

第一:在某個運作時期間,隻要對象的(字段的)變化不會影響equals方法的決策結果,那麼,在這個期間,無論調用多少次hashCode,都必須傳回同一個散列碼。

第二:通過equals調用傳回true 的2個對象的hashCode一定一樣。

第三:通過equasl傳回false 的2個對象的散列碼不需要不同,也就是他們的hashCode方法的傳回值允許出現相同的情況。

hashCode編寫指導

在編寫hashCode時,你需要考慮的是,最終的hash是個int值,而不能溢出。不同的對象的hash碼應該盡量不同,避免hash沖突。

那麼如果做到呢?下面是解決方案。

1、定義一個int類型的變量 hash,初始化為 7。

接下來讓你認為重要的字段(equals中衡量相等的字段)參入散列運,算每一個重要字段都會産生一個hash分量,為最終的hash值做出貢獻(影響)

最後把所有的分量都總和起來,注意并不是簡單的相加。選擇一個倍乘的數字31,參與計算。然後不斷地遞歸計算,直到所有的字段都參與了。

說明,以下的内容是我在google上找到并翻譯整理的,其中加入了自己的話和一些例子,便于了解,但我能保證這并不影響整體準确性。

歡迎工作一到五年的Java工程師朋友們加入Java架構開發:744677563

群内提供免費的Java架構學習資料(裡面有高可用、高并發、高性能及分布式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!