- equals用法
- hashCode用法
- 總結
- 為什麼一個類中需要兩個比較方法
- 為什麼重寫 equals 方法時必須同時重寫 hashCode 方法?
- Reference
這個並不是一個通用性程式設計問題,只屬於在Java領域內專有問題。
要做好心理準備,這是一個複雜類的問題,要解答這個問題,需要梳理清楚兩個函式和其它類之間的關係,並且它們之間的關係有點交織。
equals用法
在 Object 類中包含了 equals() 方法:
public boolean equals(Object obj) {
return (this == obj);
}
說明:
- == 用於比較
變數
所對應的記憶體中所儲存的數值 是否相同,要比較 兩個基本型別的資料(注意是基本型別) 或 兩個引用變數 是否相等。
hashCode用法
在 Object 類中還包含了 hashCode() 方法:
public native int hashCode();
請回答,為什麼 Object 類需要一個 hashCode() 方法呢?
在 Java 中,hashCode() 方法的主要作用就是為了配合雜湊表使用的。
雜湊表(Hash Table),也叫雜湊表,是一種可以透過關鍵碼值(key-value)直接訪問的資料結構,它最大的特點就是可以快速實現查詢、插入和刪除。其中用到的演算法叫做雜湊,就是把任意長度的輸入,變換成固定長度的輸出,該輸出就是雜湊值。像 MD5、SHA1 都用的是雜湊演算法。
像 Java 中的 HashSet、Hashtable、HashMap 都是基於雜湊表的具體實現。其中的 HashMap 就是最典型的代表。
大家想一下,如果沒有雜湊表,但又需要這樣一個資料結構,它裡面存放的資料是不允許重複的,它是怎麼實現的?
- 使用 equals() 方法進行逐個比較?
這種方案當然是可行的。但如果資料量特別特別大,採用 equals() 方法進行逐個對比的效率肯定很低很低,總結:能解決,但效率不高。 - 最好的解決方案就是雜湊表。總結:不光能解決,還效率高。
案例說明:
拿 HashMap 來說吧,當我們要在它裡面新增物件時,先呼叫這個物件的 hashCode() 方法,得到對應的雜湊值,然後將雜湊值和物件一起放到 HashMap 中。當我們要再新增一個新的物件時:
- 獲取物件的雜湊值;
- 和之前已經存在的雜湊值進行比較,如果不相等,直接存進去;
- 如果有相等的,再呼叫 equals() 方法進行物件之間的比較,如果相等,不存了;
- 如果不等,說明雜湊衝突了,增加一個連結串列,存放新的物件;
- 如果連結串列的長度大於 8,轉為紅黑樹來處理。
就這麼一套下來,呼叫 equals() 方法的頻率就大大降低了。也就是說,只要雜湊演算法足夠的高效,把發生雜湊衝突的頻率降到最低,雜湊表的效率就特別的高。
總結
== 用於比較變數所對應的記憶體中所儲存的數值是否相同,要比較兩個基本型別的資料(注意是基本型別)或兩個 引用變數是否相等,只能用==運算子。
equals 比較的是值和地址,如果沒有重寫equals方法,其作用與==相同;
在String類中,重寫了equals方法,比較的是值
是否相等;
hashCode用於雜湊資料結構中的hash值計算;
equals兩個物件相等,那hashcode一定相等,hashcode相等,不一定是同一個物件(hash衝突現象);
hashCode 一般與 equals 一起使用,兩個物件作「相等」比較時,因判斷 hashCode 是判斷 equals 的先決條件.
為什麼一個類中需要兩個比較方法
因為重寫的 equals() 裡一般比較的比較全面比較複雜,這樣效率就比較低,而利用hashCode()進行對比,則只要生成一個 hash 值進行比較就可以了,效率很高,那麼 hashCode() 既然效率這麼高為什麼還要 equals() 呢?
-
因為 hashCode() 並不是完全可靠,有時候不同的物件他們生成的 hashcode 也會一樣(hash衝突),所以 hashCode()只能說是大部分時候可靠,並不是絕對可靠。
-
equals() 相等的兩個物件他們的 hashCode() 肯定相等,也就是用 equals() 對比是絕對可靠的。
為什麼重寫 equals 方法時必須同時重寫 hashCode 方法?
可以先看看Java這B 給出的一些建議,就是事前就規定好了...
public class Object {
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* `java.util.HashMap`.
*
* The general contract of `hashCode` is:
*
* a) Whenever it is invoked on the same object more than once during
* an execution of a Java application, the `hashCode` method must
* consistently return the same integer, provided no information
* used in `equals` comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
*
* b) If two objects are equal according to the `equals(Object)` method,
* then calling the `hashCode` method on each of the two objects must
* produce the same integer result.
*
* c) It is not required that if two objects are unequal according to the
* `equals(Object)` method, then calling the `hashCode` method on each of
* the two objects must produce distinct integer results.
* However, the programmer should be aware that producing distinct integer
* results for unequal objects may improve the performance of hash tables.
*/
@IntrinsicCandidate
public native int hashCode();
/**
* Indicates whether some other object is "equal to" this one.
*
* @apiNote
* It is generally necessary to override the `hashCode` method whenever this
* method is overridden, so as to maintain the general contract for the `hashCode`
* method, which states that equal objects must have equal hash codes.
*/
public boolean equals(Object obj) {
return (this == obj);
}
}
上面介紹了 hashCode 方法註釋上列出的三個通用約定,equals 方法的註釋上也有這麼一句話:「每當重寫 equals 方法時,都需要重寫 hashCode 方法,這樣才沒有破壞 hashCode 方法的通用約定,即:兩個物件為 Equal 的話(呼叫 equals 方法為 true), 那麼這兩個物件分別呼叫 hashCode 方法也需要返回相同的雜湊值」。
所以只重寫 equals 方法不重寫 hashCode 方法的話,可能會造成兩個物件呼叫 equals 方法為 true,而 hashCode 值不同的情形,這樣即可能造成異常的行為。
這個情形是什麼?
兩個內容相等的Person物件p1和p2的hashCode()不同,是因為在Person類中沒有重寫hashCode()方法,它們使用的是Object類繼承下來的hashCode()方法的預設實現。
在Object類中,hashCode()方法的預設實現是將物件的記憶體地址值作為雜湊碼返回。
總結:
就是一個約定而已。也是為了邏輯的自洽。
Reference
Java hashCode方法深入解析
https://www.javabetter.cn/basic-extra-meal/hashcode.html
Java:為什麼重寫 equals 方法時必須同時重寫 hashCode 方法?
https://leileiluoluo.com/posts/always-override-hashcode-when-override-equals.html