【Java】浅析equals()和hashCode()
總覽equals( ) 方法
-
理解equals()方法和==運算符的區別是非常重要的。
- 默認情況下(即沒有被重寫時)equals()只能比較引用類型,"=="既能 比較引用類型又能比較基本類型。
- equals()方法從Object類繼承,即比較對象引用的值
- 一般都被子類方法覆蓋,不再比較引用的值
-
"=="運算符:
- 比較基本數據類型:相當于算術等號。
- 比較引用數據類型:比較引用的值,不能被覆蓋。
-
通常情況,子類要重寫equals( ),改變它的含義。所以有的類中 equals( )是比較地址,有的類中該方法就不比較地址,具體的,就看子類新定義的該方法的規定。建議看看包裝類中的equals()方法,蠻有趣的。
-
在Java中有個規定:如果equals( )返回兩個對象是相等的,那這兩個對象上調用hashCode( )返回的整數必須相等。否則在使用Hash類型集合時就會產生錯誤。
-
注意:覆蓋equals( )方法同時,還要記得覆蓋hashCode( )方法。 需要說明,如果equals( )返回兩個對象不等,它們的hashCode( )也 可以返回相同的整數。但是最好讓它們的hashCode( )返回不同的整 數,這有利于提高Hash類型集合的性能。
-
重寫equals方法時,一定要重寫hashcode()方法嗎?
-
hashcode的調用的條件:
- 想往map里面放一個類作為map的鍵值,這個類又是自己設計的;
- 雖然類不是自己寫的,但是你修改了這個類的equals方法;
-
如果滿足上述調用條件,就要注意重寫hashcode方法。
這樣 當你往map 里放值得時候,系統會調用這個對象的.hashcode()方法來生成相應的 hash值,來映射相應的對象。
-
-
如果同一個類的兩個對象的屬性值相等,那么他們的hashcode一定相等嗎?這個要看你具體如何實現你的hashcode,如果你希望他們的值一樣hashcode也一樣,你就可以這樣實現。 但是hashcode的實現,一般要滿足幾個特征,比如自反性、對稱性、傳遞性那些。
等價關系與equals( )
離散數學的知識:
設R是A上關系,若R是自反的、對稱的和傳遞的,則稱R是A中的等價關系。
若a,b∈A,且aRb,則稱a與b等價若 a,b\in A,且aRb,則稱a與b等價若a,b∈A,且aRb,則稱a與b等價。
equals() 其實就是一種等價關系,它指定了某種方法(默認的hashCode()、重寫的hashCode()、自定義的equals()判據等)來判定這種等價關系。
我們自己想要重寫equals(),為了保證這種等價關系,就需要維護自反性、對稱性、傳遞性。
其實,簡單的測試還不太夠,最好是有形式化的證明。
這篇文章中就涉及了equals()的自反性、對稱性、傳遞性,做了簡單的測試……
其實我也很無奈,這是我在準備大學里一門核心專業課考試的前一晚總結的,內心真正比較喜歡的文章,but沒人看,Readers更喜歡比較浮夸的文章
IDE自動生成equals( )和hashCode( )
如果你確實懶得重寫equals()和hashCode(),IDE支持自動生成這兩個方法。
首先,Person類已經完成到了這個程度:
import java.io.Serializable;public class Person implements Serializable {private static final long serialVersionUID = 1L;private Integer id;private String name;private Integer age;public Person(Integer id, String name, Integer age) {this.id = id;this.name = name;this.age = age;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "Person{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + '}';}}Eclipse
IntelliJ IDEA
String的hashCode( )
String有hash這個實例變量,它的定義如下:
private int hash; // Default to 0它緩存了hashCode()方法的值,也就是說,第一次調用hashCode()的時候,會把結果保存在hash這個變量中,以后再調用就直接返回保存的值。
我們來看下String類的hashCode方法,代碼如下:
public int hashCode() {int h = hash;if (h == 0 && value.length > 0) {char val[] = value;for (int i = 0; i < value.length; i++) {h = 31 * h + val[i];}hash = h;}return h; }如果緩存的hash不為0,就直接返回了,否則根據字符數組中的內容計算hash,計算方法是:
s[0]?31(n?1)+s[1]?31(n?2)+?+s[n?1]?30s[0]*31^{(n-1)} + s[1]*31^{(n-2)} + \cdots + s[n-1] * 3^0s[0]?31(n?1)+s[1]?31(n?2)+?+s[n?1]?30
s表示字符串,s[0]表示第一個字符,n表示字符串長度,s[0]?31(n?1)s[0]*31^{(n-1)}s[0]?31(n?1)表示31的n-1次方再乘以第一個字符的值。
為什么要用這個計算方法呢?這個式子中,hash值與每個字符的值有關,每個位置乘以不同的值,hash值與每個字符的位置也有關。使用31大概是因為兩個原因,一方面可以產生更分散的散列,即不同字符串hash值也一般不同,另一方面計算效率比較高,31* h 與 32 * h - h 即 (h<<5)-h 等價,可以用更高效率的移位和減法操作代替乘法操作。
在Java中,普遍采用以上思路來實現hashCode,也就是所謂的"33-Times"算法,多看源碼,其實Java很多地方都是這樣的思路。
總結
以上是生活随笔為你收集整理的【Java】浅析equals()和hashCode()的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 大数高精加减乘除(洛谷P1601、P21
- 下一篇: 银行贷款(洛谷P1163题题解,Java