equals()
方法和 hashCode()
方法詳解
equals()
hashCode()
1. Object
類中 equals()
方法源代碼如下所示:
Object
equals()
/**
* Object類中的equals()方法
*/
public boolean equals(Object obj) {
return (this == obj);
}
由以上源代碼知,類中的
Object
方法是直接使用
equals()
運算符來判斷兩個對象相等的。
==
- 引用類型變量使用
時,比較的是引用類型變量指向的對象的記憶體位址
==
- 基本類型使用
時,比較值
==
Objcect
類中的
hashCode
源代碼如下:
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <p>
* The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code 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.
* <li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link java.lang.Object#equals(java.lang.Object)}
* method, then calling the {@code 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.
* </ul>
* <p>
* As much as is reasonably practical, the hashCode method defined by
* class {@code Object} does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java™ programming language.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
public native int hashCode();// java8中的hashCode方法,
上面的注釋中有說明如下幾點:
- 對象的
值通常是根據對象的記憶體位址計算得來
hashCode
- 兩個對象
結果為
equals()
時,兩個對象的
true
值一定相等,不同對象的
hashCode
不等
hashCode
辨別此方法不是
native
語言實作
java
Object
類中的
toString()
方法源代碼如下:
public String toString() {
// 從這裡就能看出列印對象時不重寫toString()方法時,就會列印出對象的hashCode值
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
2. String
類中 equals()
方法和 hashCode()
方法
String
equals()
hashCode()
String
類中部分源代碼如下所示:
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
/**
* 無參構造方法
*/
public String() {
this.value = "".value;
}
/**
* 有參構造方法
*/
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
/**
*String類重寫的equals方法
*/
public boolean equals(Object anObject) {
if (this == anObject) {// 此處的this指向a.equals(b)的a對象,即誰調用指向誰
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
/**
* Returns a hash code for this string. The hash code for a
* {@code String} object is computed as
* <blockquote><pre>
* s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
* </pre></blockquote>
* using {@code int} arithmetic, where {@code s[i]} is the
* <i>i</i>th character of the string, {@code n} is the length of
* the string, and {@code ^} indicates exponentiation.
* (The hash value of the empty string is zero.)
*
* @return a hash code value for this object.
*/
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;
}
從上面的源碼中,我們不難發現
String
類已經重寫了
equals()
方法和
hashCode()
方法。
String
類重寫的
equals()
方法判斷流程如下:
- 使用
來判斷兩個對象的記憶體位址是否相同,相同傳回
==
true;
- 如果兩個對象的記憶體位址不同,程式繼續往下走,判斷另一個對象是否是
類型的;
String
- 如果比較對象不是
類型,直接傳回
String
;
false
- 如果是
類型的,進行類型強轉;
String
- 比較兩個
的字元數組長度,如果長度不同,傳回
String
;
false
- 利用
循環來逐位比較字元是否相等,直到循環結束,所有字元都相等,則傳回
while
,否則傳回
true
;
false
下面來看一下重寫的
hashCode()
方法。
- 首先
類中定義了一個
String
類型的變量
int
用來緩存
hash
對象的
String
值;
hash
- 如果目前調用
方法的
hashCode()
對象在常量池沒有找到,并且該對象的
String
長度大于 ,則繼續往下走,否則傳回 ;即
length
類預設
String
字元串的
""
值為 ;
hashCode()
- 周遊字元數組,擷取每一個字元的
碼表對應的值 和之前的
ASCII
值相加,這樣就保證了相同的字元串的
hash
傳回值相同,計算公式在注釋裡已經寫出來了:
hashCode()
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
- 将計算出來的結果儲存到
變量中,并傳回該值;
hash
這裡為什麼要乘以
31
呢?原因是為了性能,不僅僅指降低了計算速度,也降低了哈希沖突的機率。
哈希沖突:此處指不同的字元串生成了相同的 hashCode
值。
31
是一個奇素數。如果乘數是偶數,并且乘法溢出的話,資訊就會丢失,因為與2相乘等價于移位運算(低位補
)。使用素數的好處并不很明顯,但是習慣上使用素數來計算散列結果。
31
有個很好的性能,即用移位和減法來代替乘法,可以得到更好的性能:
31 * i == (i << 5)- i
, 現代的
VM
可以自動完成這種優化。這個公式可以很簡單的推導出來。 ---- 《
Effective Java
》
素數:質數又稱素數,指在一個大于1的自然數中,除了1和此整數自身外,沒法被其他自然數整除的數。