哈希值 & 哈希表

哈希值

是一個十進制的整數,由系統隨機給出,就是是對象的地址值(十六進制)也稱邏輯地址,但非對象的物理地址。

獲取方法

在Object類有一個方法,可以獲取對象的哈希值

  • public native int hashCode():返回該對象的哈希碼值。

    native:代表該方法調用的是本地操作系統的方法

hashCode方法梳理

對象的哈希值

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方法來決定的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章