深入理解equals和hashCode關係和區別

2022-09-11 12:36:19 字數 3137 閱讀 8066

改寫equals時總是要改寫hashcode

分享一波:程式設計師賺外快-必看的巔峰乾貨

為什麼要說equals和hashcode這兩個東西,一來是因為有不少小夥伴面試時被問過這個東西,二來則是因為如果了解了這兩個東西的原理,那麼實際的開發過程中,對效率和容錯率上還是能幫上很大的忙!

很多人把他們放在一起比較,那我們首先要想到的是,他們肯定有大致相同的作用,和一些細小的區別。

先說說他們相同的作用:equals和hashcode方法都是用來判斷兩個物件的值是否相等,請記住這裡說的相等僅僅說的是兩個物件的值,和他們的引用無關

為啥這樣說,因為hashcode有極小概率會判斷錯,而equals的判斷結果是百分百正確的

相信看到這裡已經有朋友有疑問了,既然hashcode方法不能準確判斷,那我們為什麼還要用它?哈哈哈,因為我們無法忍受丟棄他的另乙個優點,就是效率高,接著往下看,我們慢慢捋一捋。

先說上面的第一點:判斷物件的相等的方式不一樣

1.equals:重寫的equals方法,比如string 的equals方法,如圖:

他最終的目的迴圈判斷兩個物件的每乙個字元是否相等,所有字元全部對應相等,那他們的值肯定也就相等了,這是equals的判斷方法

hashcode的判斷方法:hashcode是通過用hash演算法來計算具體物件例項,得到斌返回乙個hashcode值。通過比較hashcode值是否相等來判斷兩個物件是否相等,

以j**a.lang.object來理解,jvm每new乙個object,它都會將這個object丟到乙個hash雜湊表中去,這樣的話,下次做object的比較或者取這個物件的時候,它會根據物件的hashcode再從hash表中取這個物件。這樣做的目的是提高取物件的效率。具體過程是這樣:

new object(),jvm根據這個物件的hashcode值,放入到對應的hash表對應的key上,如果不同的物件確產生了相同的hash值,也就是發生了hash key相同導致衝突的情況,那麼就在這個hash key的地方產生乙個鍊錶,將所有產生相同hashcode的物件放到這個單鏈表上去,串在一起。

比較兩個物件的時候,首先根據他們的hashcode去hash表中找他的物件,當兩個物件的hashcode相同,那麼就是說他們這兩個物件放在hash表中的同乙個key上,那麼他們一定在這個key上的鍊錶上。那麼此時就只能根據object的equal方法來比較這個物件是否equal。當兩個物件的hashcode不同的話,肯定他們不能equal.

可能經過上面理論的講一下大家都迷糊了,我也看了之後也是似懂非懂的。下面我舉個例子詳細說明下。

list是可以重複的,set是不可以重複的。那麼set儲存資料的時候是怎樣判斷存進的資料是否已經存在。使用equals()方法呢,還是hashcode()方法。

假如用equals(),那麼儲存乙個元素就要跟已存在的所有元素比較一遍,比如已存入100個元素,那麼存101個元素的時候,就要呼叫equals方法100次。

但如果用hashcode()方法的話,他就利用了hash演算法來儲存資料的。

這樣的話每存乙個資料就呼叫一次hashcode()方法,得到乙個hashcode值及存入的位置。如果該位置不存在資料那麼就直接存入,否則呼叫一次equals()方法,不相同則存,相同不存。這樣下來整個儲存下來不需要呼叫幾次equals方法,雖然多了幾次hashcode方法,但相對於前面來講效率高了不少。

上面開始的時候我著重說了重寫的equals方法,為什麼要重寫呢?

因為object的equal方法預設是兩個物件的引用的比較,意思就是指向同一記憶體,位址則相等,否則不相等;如果你現在需要利用物件裡面的值來判斷是否相等,則過載equal方法。

說道這個地方我相信很多人會有疑問,相信大家都被string物件的equals()方法和"= ="糾結過一段時間,當時我們知道string物件中equals方法是判斷值的,而= =是位址判斷。

那照這麼說equals怎麼會是位址的比較呢?

那是因為實際上jdk中,string、math等封裝類都對object中的equals()方法進行了重寫。

我們先看看object中equals方法的原始碼:

我們都知道所有的物件都擁有標識(記憶體位址)和狀態(資料),同時「==」比較兩個物件的的記憶體位址,所以說使用object的equals()方法是比較兩個物件的記憶體位址是否相等,即若object1.equals(object2)為true,則表示equals1和equals2實際上是引用同乙個物件。雖然有時候object的equals()方法可以滿足我們一些基本的要求,但是我們必須要清楚我們很大部分時間都是進行兩個物件的比較,這個時候object的equals()方法就不可以了,所以才會有string這些類對equals方法的改寫,依次類推double、integer、math。。。。等等這些類都是重寫了equals()方法的,從而進行的是內容的比較。希望大家不要搞混了。

j**a.lnag.object中對hashcode的約定:

在乙個應用程式執行期間,如果乙個物件的equals方法做比較所用到的資訊沒有被修改的話,則對該物件呼叫hashcode方法多次,它必須始終如一地返回同乙個整數。

如果兩個物件根據equals(object o)方法是相等的,則呼叫這兩個物件中任一物件的hashcode方法必須產生相同的整數結果。

如果兩個物件根據equals(object o)方法是不相等的,則呼叫這兩個物件中任乙個物件的hashcode方法,不要求產生不同的整數結果。但如果能不同,則可能提高雜湊表的效能。

根據上乙個問題,實際上我們已經能很簡單的解釋這一點了,比如改寫string中的equals為基於內容上的比較而不是記憶體位址的話,那麼雖然equals相等,但並不代表記憶體位址相等,由hashcode方法的定義可知記憶體位址不同,沒改寫的hashcode值也可能不同。所以違背了第二條約定。

又如new乙個物件,再new乙個內容相等的物件,呼叫equals方法返回的true,但他們的hashcode值不同,將兩個物件存入hashset中,會使得其中包含兩個相等的物件,因為是先檢索hashcode值,不等的情況下才會去比較equals方法的。

深入理解C語言 深入理解指標

關於指標,其是c語言的重點,c語言學的好壞,其實就是指標學的好壞。其實指標並不複雜,學習指標,要正確的理解指標。指標也是一種變數,占有記憶體空間,用來儲存記憶體位址 指標就是告訴編譯器,開闢4個位元組的儲存空間 32位系統 無論是幾級指標都是一樣的 p操作記憶體 在指標宣告時,號表示所宣告的變數為指...

mysql 索引深入理解 深入理解MySql的索引

為什麼索引能提高查詢速度 先從 mysql的基本儲存結構說起 mysql的基本儲存結構是頁 記錄都存在頁裡邊 各個資料頁可以組成乙個雙向鍊錶每個資料頁中的記錄又可以組成乙個單向鍊錶 每個資料頁都會為儲存在它裡邊兒的記錄生成乙個頁目錄,在通過主鍵查詢某條記錄的時候可以在頁目錄中使用二分法快速定位到對應...

深入理解C語言 深入理解指標

關於指標,其是c語言的重點,c語言學的好壞,其實就是指標學的好壞。其實指標並不複雜,學習指標,要正確的理解指標。指標也是一種變數,占有記憶體空間,用來儲存記憶體位址 指標就是告訴編譯器,開闢4個位元組的儲存空間 32位系統 無論是幾級指標都是一樣的 p操作記憶體 在指標宣告時,號表示所宣告的變數為指...