歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Java中equals()與hashCode()方法完全解惑

Java中equals()與hashCode()方法完全解惑

日期:2017/3/1 10:52:30   编辑:Linux編程

1. 首先equals()和hashcode()這兩個方法都是從object類中繼承過來的。
equals()方法在object類中定義如下:
public boolean equals(Object obj) {
return (this == obj);
}
很明顯是對兩個對象的地址值進行的比較(即比較引用是否相同)。但是我們必需清楚,當String 、Math、還有Integer、Double。。。。等這些封裝類在使用equals()方法時,已經覆蓋了object類的equals()方法。比如在String類中如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n– != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
很明顯,這是進行的內容比較,而已經不再是地址的比較。依次類推Double、Integer、Math。。。。等等這些類都是重寫了equals()方法的,從而進行的是內容的比較。當然了基本類型是進行值的比較,這個沒有什麼好說的。
我們還應該注意,Java語言對equals()的要求如下,這些要求是必須遵循的:
• 對稱性:如果x.equals(y)返回是“true”,那麼y.equals(x)也應該返回是“true”。
• 反射性:x.equals(x)必須返回是“true”。
• 類推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那麼z.equals(x)也應該返回是“true”。
• 還有一致性:如果x.equals(y)返回是“true”,只要x和y內容一直不變,不管你重復x.equals(y)多少次,返回都是“true”。
• 任何情況下,x.equals(null),永遠返回是“false”;x.equals(和x不同類型的對象)永遠返回是“false”。
以上這五點是重寫equals()方法時,必須遵守的准則,如果違反會出現意想不到的結果,請大家一定要遵守。
2. 其次是hashcode() 方法,在object類中定義如下:
public native int hashCode();
說明是一個本地方法,它的實現是根據本地機器相關的。當然我們可以在自己寫的類中覆蓋hashcode()方法,比如String、Integer、Double。。。。等等這些類都是覆蓋了hashcode()方法的。例如在String類中定義的hashcode()方法如下:
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;

for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
解釋一下這個程序(String的API中寫到):
s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]
使用 int 算法,這裡 s[i] 是字符串的第 i 個字符,n 是字符串的長度,^ 表示求冪。(空字符串的哈希碼為 0。)

首先,想要明白hashCode的作用,你必須要先知道Java中的集合。  
總的來說,Java中的集合(Collection)有兩類,一類是List,再有一類是Set。
你知道它們的區別嗎?前者集合內的元素是有序的,元素可以重復;後者元素無序,但元素不可重復。
那麼這裡就有一個比較嚴重的問題了:要想保證元素不重復,可兩個元素是否重復應該依據什麼來判斷呢?
這就是Object.equals方法了。但是,如果每增加一個元素就檢查一次,那麼當元素很多時,後添加到集合中的元素比較的次數就非常多了。
也就是說,如果集合中現在已經有1000個元素,那麼第1001個元素加入集合時,它就要調用1000次equals方法。這顯然會大大降低效率。
於是,Java采用了哈希表的原理。哈希(Hash)實際上是個人名,由於他提出一哈希算法的概念,所以就以他的名字命名了。
哈希算法也稱為散列算法,是將數據依特定算法直接指定到一個地址上。如果詳細講解哈希算法,那需要更多的文章篇幅,我在這裡就不介紹了。
初學者可以這樣理解,hashCode方法實際上返回的就是對象存儲的物理地址(實際可能並不是)。
這樣一來,當集合要添加新的元素時,先調用這個元素的hashCode方法,就一下子能定位到它應該放置的物理位置上。
如果這個位置上沒有元素,它就可以直接存儲在這個位置上,不用再進行任何比較了;如果這個位置上已經有元素了,
就調用它的equals方法與新元素進行比較,相同的話就不存了,不相同就散列其它的地址。
所以這裡存在一個沖突解決的問題。這樣一來實際調用equals方法的次數就大大降低了,幾乎只需要一兩次。
所以,Java對於eqauls方法和hashCode方法是這樣規定的:
1、如果兩個對象相同,那麼它們的hashCode值一定要相同;2、如果兩個對象的hashCode相同,它們並不一定相同 上面說的對象相同指的是用eqauls方法比較。
你當然可以不按要求去做了,但你會發現,相同的對象可以出現在Set集合中。同時,增加新元素的效率會大大下降。
3.這裡我們首先要明白一個問題:
equals()相等的兩個對象,hashcode()一定相等;
equals()不相等的兩個對象,卻並不能證明他們的hashcode()不相等。換句話說,equals()方法不相等的兩個對象,hashcode()有可能相等。(我的理解是由於哈希碼在生成的時候產生沖突造成的)。
反過來:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。解釋下第3點的使用范圍,我的理解是在object、String等類中都能使用。在object類中,hashcode()方法是本地方法,返回的是對象的地址值,而object類中的equals()方法比較的也是兩個對象的地址值,如果equals()相等,說明兩個對象地址值也相等,當然hashcode()也就相等了;在String類中,equals()返回的是兩個對象內容的比較,當兩個對象內容相等時,
Hashcode()方法根據String類的重寫(第2點裡面已經分析了)代碼的分析,也可知道hashcode()返回結果也會相等。以此類推,可以知道Integer、Double等封裝類中經過重寫的equals()和hashcode()方法也同樣適合於這個原則。當然沒有經過重寫的類,在繼承了object類的equals()和hashcode()方法後,也會遵守這個原則。

Copyright © Linux教程網 All Rights Reserved