equals() 與hasCode() 都定義在Object.java中。equals 默認使用「==」來比較對象。java
public boolean equals(Object obj) { return (this == obj); } public native int hashCode();
在對於hashCode()的javadoc中明確代表: hashCode是用在hash表中的。 equals相同,hashCode必定相同(但hashCode相同,equals不必定相同),這樣有利於提升hash表的效率。算法
eg. 驗證: hashCode相同,equals不必定相同源碼分析
public static void main(String[] args) { Long a = 1L; Integer b = 1; System.out.println(b.hashCode() == a.hashCode()); // true System.out.println(b.equals(a)); // false }
執行結果: true、 false。ui
對於String: this
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; } public boolean equals(Object anObject) { if (this == anObject) { 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; }
對於Boolean:
hashCode() 爲定值: true->1231! fale -> 1237。 equals() 先斷定是不是本類的實例再比較值。spa
public int hashCode() { return value ? 1231 : 1237; } public boolean equals(Object obj) { if (obj instanceof Boolean) { return value == ((Boolean)obj).booleanValue(); } return false; }
對於比Integer範圍小的short、byte、char :
hashCode() 直接強轉int。 equals() 先斷定是不是本類的實例再比較值。.net
public int hashCode() { return (int)value; } public boolean equals(Object obj) { if (obj instanceof Short) { return value == ((Short)obj).shortValue(); } return false; }
對於比Integer範圍大的Long、float、double:
hashCode(): 則須要處理轉換成int。double比較特殊些:是先處理爲long,再用long取hashCode()的方式轉int;
equals(): Long是直接比較數值; 而float、double是要轉成int進行比較(按javadoc中所述 須要考慮NaN的問題),NaN直接返回定值。code
public static void main(String[] args) { float a = Float.NaN; Float b = Float.NaN; System.out.println(a == b); // flase System.out.println(b.equals(b)); // true float a2 = 1f; Float b2 = 1f; System.out.println(a2 == b2); // true System.out.println(b2.equals(b2)); // true }
執行結果: false、true、true、true。 對象
NaN與任何浮點數(包括自身)的比較結果都爲假,這是NaN獨有的特性,因此可使用與本身相比來肯定當前數值是否是一個正常的數.blog
結論: 若是僅僅考慮非NaN的問題能夠直接用floatValue()、doubleValue()方法。但由於float與double有NaN因此須要考慮!
源碼以下:
Long: public static int hashCode(long value) { return (int)(value ^ (value >>> 32)); } public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; } Float: /** * A constant holding a Not-a-Number (NaN) value of type * {@code float}. It is equivalent to the value returned by * {@code Float.intBitsToFloat(0x7fc00000)}. */ public static final float NaN = 0.0f / 0.0f; public static int hashCode(float value) { return floatToIntBits(value); } public boolean equals(Object obj) { return (obj instanceof Float) && (floatToIntBits(((Float)obj).value) == floatToIntBits(value)); } public static int floatToIntBits(float value) { int result = floatToRawIntBits(value); // Check for NaN based on values of bit fields, maximum // exponent and nonzero significand. if ( ((result & FloatConsts.EXP_BIT_MASK) == FloatConsts.EXP_BIT_MASK) && (result & FloatConsts.SIGNIF_BIT_MASK) != 0) result = 0x7fc00000; return result; } Double: public int hashCode() { long bits = doubleToLongBits(value); return (int)(bits ^ (bits >>> 32)); } public static long doubleToLongBits(double value) { long result = doubleToRawLongBits(value); // Check for NaN based on values of bit fields, maximum // exponent and nonzero significand. if ( ((result & DoubleConsts.EXP_BIT_MASK) == DoubleConsts.EXP_BIT_MASK) && (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L) result = 0x7ff8000000000000L; return result; } /** * A constant holding a Not-a-Number (NaN) value of type * {@code double}. It is equivalent to the value returned by * {@code Double.longBitsToDouble(0x7ff8000000000000L)}. */ public static final double NaN = 0.0d / 0.0;
維護 hashCode 方法的常規協定,該協定聲明相等對象必須具備相等的哈希碼。
hashcode是用於散列數據的快速存取,如利用HashSet、HashMap、Hashtable類來存儲數據時,都是根據存儲對象的hashcode值來進行判斷是否相同的。
hashSet的核心就是經過HashMap的key來實現的
/** * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has * default initial capacity (16) and load factor (0.75). */ public HashSet() { map = new HashMap<>(); } public boolean add(E e) { return map.put(e, PRESENT)==null; }
經過hashMap的源碼可見: hash(Object key)對於定位對象在table的位置相當重要,其中就會使用對象的hashCode值。
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
因此在老版本(1.7)hashMap的實現中對於hash定位相同的值將使用拉鍊法存儲:存儲table位置上的是一個Map.Entry對象鏈表。新值對若是hash定位相同,equals不一樣,則封裝成Map.Entry對象將插入進該鏈表的頭部,next指向鏈表原頭部;該鏈表查找算法的時間複雜度爲0(n)。而在1.8改成當鏈表長度超過TREEIFY_THRESHOLD(默認 8) <實則是9>則轉爲紅黑樹存儲,因紅黑樹的特性,查找算法的時間複雜度爲O(log2 n)。(hashTable的處理仍是拉鍊法)