哈希值
是一個十進制的整數,由系統隨機給出,就是是對象的地址值(十六進制)也稱邏輯地址,但非對象的物理地址。
獲取方法
在Object類有一個方法,可以獲取對象的哈希值
-
public native int hashCode()
:返回該對象的哈希碼值。native
:代表該方法調用的是本地操作系統的方法
對象的哈希值
public class Demo01HashCode {
public static void main(String[] args) {
// Person繼承了Object類,所以可以使用Object類的hashCode方法
Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person("小明",18);
int h1 = p1.hashCode();
int h2 = p2.hashCode();
int h3 = p3.hashCode();
System.out.println(h1);
System.out.println(h2);
System.out.println(h3);
System.out.println(h1 == h2);
System.out.println(h1 == h3);
/*
toString方法的源碼:
return getClass().getName() + "@" + Integer.toHexString(hashCode());
*/
// 物理地址不相同
System.out.println(p1);
System.out.println(p2);
System.out.println(p3);
System.out.println(p1 == p2);
System.out.println(p1 == p3);
}
}
重寫hashCode方法
public class Person {
// ...省略
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
結果:
未重寫hashCode
方法:
======邏輯地址、判斷=======
1355531311
1967205423
42121758
false
false
======地址值、判斷=======
com.luis.demo02.hashCode.Person@50cbc42f
com.luis.demo02.hashCode.Person@75412c2f
com.luis.demo02.hashCode.Person@282ba1e
// 物理地址不相同
false
false
==================
重寫後:
======邏輯地址、判斷=======
961
961
23458772
true
false
======物理地址、判斷=======
com.luis.demo02.hashCode.Person@3c1
com.luis.demo02.hashCode.Person@3c1
com.luis.demo02.hashCode.Person@165f3d4
false
false
==================
字符串類的哈希值
字符串類重寫了hashCode
方法,所以s1、s2
的哈希值均爲:96354
有兩個元素不同,但是哈希值相同(特例):哈希衝突
- 重地:1179395
- 通話:1179395
代碼:
public class Demo01HashCode {
public static void main(String[] args) {
String s1 = new String("abc");
String s2 = new String("abc");
String s3 = new String("aBc");
String s4 = "abc";
String s5 = "abc";
String s6 = "aBc";
System.out.println(s1.hashCode()); // 96354
System.out.println(s2.hashCode()); // 96354
System.out.println(s2.hashCode()); // 95362
System.out.println(s4.hashCode()); // 96354
System.out.println(s5.hashCode()); // 96354
System.out.println(s6.hashCode()); // 95362
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s1 == s4);
System.out.println(s1 == s5);
System.out.println(s1 == s6);
System.out.println(s4 == s5); // true,其餘爲false
System.out.println(s4 == s6);
// 兩個元素不同,但是哈希值相同(特例):哈希衝突
System.out.println("重地".hashCode()); // 1179395
System.out.println("通話".hashCode()); // 1179395
}
}
圖表理解
哈希表 【JDK1.8】
在JDK1.8之前,哈希表底層採用數組+鏈表實現,即使用鏈表處理衝突,同一hash值的鏈表都存儲在一個鏈表裏。但是當位於一個桶中的元素較多,即hash值相等的元素較多時,通過key值依次查找的效率較低。
而JDK1.8中,哈希表存儲採用數組+鏈表+紅黑樹實現,當鏈表長度超過閾值(8)時,將鏈表轉換爲紅黑樹,這樣大大減少了查找時間。
簡單的來說,哈希表是由數組+鏈表+紅黑樹(JDK1.8增加了紅黑樹部分)實現的,如下圖所示。
看到這張圖就有人要問了,這個是怎麼存儲的呢?爲了方便理解我們結合一個存儲流程圖來說明一下:
總而言之,JDK1.8引入紅黑樹大程度優化了HashMap的性能,那麼對於我們來講保證HashSet集合元素的唯一,其實就是根據對象的hashCode和equals方法來決定的。