equals()
方法和hashCode()
方法詳解Object
類中equals()
方法源代碼以下所示:/** * Object類中的equals()方法 */ public boolean equals(Object obj) { return (this == obj); }
由以上源代碼知,
Object
類中的equals()
方法是直接使用==
運算符來判斷兩個對象相等的。java
- 引用類型變量使用
==
時,比較的是引用類型變量指向的對象的內存地址- 基本類型使用
==
時,比較值
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()
方法源代碼以下:app
public String toString() { // 從這裏就能看出打印對象時不重寫toString()方法時,就會打印出對象的hashCode值 return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
String
類中equals()
方法和hashCode()
方法
String
類中部分源代碼以下所示:ide
/** 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()
方法。ui
- 首先
String
類中定義了一個int
類型的變量hash
用來緩存String
對象的hash
值;- 若是當前調用
hashCode()
方法的String
對象在常量池沒有找到,而且該對象的length
長度大於0
,則繼續往下走,不然返回0
;即String
類默認""
字符串的hashCode()
值爲0
;- 遍歷字符數組,獲取每個字符的
ASCII
碼錶對應的值 和以前的hash
值相加,這樣就保證了相同的字符串的hashCode()
返回值相同,計算公式在註釋裏已經寫出來了:s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
- 將計算出來的結果保存到
hash
變量中,並返回該值;
這裏爲何要乘以31
呢?緣由是爲了性能,不單單指下降了計算速度,也下降了哈希衝突的機率。this
哈希衝突:此處指不一樣的字符串生成了相同的
hashCode
值。code
31
是一個奇素數。若是乘數是偶數,而且乘法溢出的話,信息就會丟失,由於與2相乘等價於移位運算(低位補0
)。使用素數的好處並不很明顯,可是習慣上使用素數來計算散列結果。 31
有個很好的性能,即用移位和減法來代替乘法,能夠獲得更好的性能: 31 * i == (i << 5)- i
, 現代的 VM
能夠自動完成這種優化。這個公式能夠很簡單的推導出來。 ---- 《Effective Java
》
素數:質數又稱素數,指在一個大於1的天然數中,除了1和此整數自身外,無法被其餘天然數整除的數。