淺析Java中的雜湊值HashCode的作用及用法

qq_45309010發表於2020-11-23

0.目的

1.什麼是hashCode及如何計算。

2.hashCode在集合HashSet中的作用?

1.雜湊值如何計算

1.1沒有重寫hashCode方法

​ 沒有重寫則會繼承父類Object中的方法,我們看一下Ojbect中的hashCode和equals方法

public native int hashCode();//native方法
public boolean equals(Object obj) {
    return (this == obj);//直接根據兩個物件的地址值來判斷是否相等
}

hashCode()是根據記憶體地址計算Hash值

public class Test01 {
    String name ="a";
    int age=10;

    public static void main(String[] args) {
        Test01 t = new Test01();
        System.out.println(t.hashCode());// 1554874502
        System.out.println(System.identityHashCode(t));// 1554874502
    }
}
// 若沒有重寫override方法,hashCode是根據記憶體地址計算Hash值

2.2重寫hashCode方法(idea)

​ 以下是idea自動生成hashCode方法,idea會同時生成equals()hashCode()方法,
​ idea生成的HashCode()方法是根據物件中的Field欄位值計算出的雜湊值

public class Test01 {
    String name;
    int age;
    Test01(){}

    Test01(String name,int age){
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if(this == o) return true;
        if(o == null || getClass() != o.getClass()) return false;

        Test01 test01 = (Test01) o;

        if(age != test01.age) return false;
        return name != null ? name.equals(test01.name) : test01.name == null;
    }

    @Override
    public int hashCode() {
        int result = name != null ? name.hashCode() : 0;
        result = 31 * result + age;
        return result;
    }

    public static void main(String[] args) {
        Test01 t = new Test01();
        System.out.println(t.hashCode());// 0; 由於age預設0,name預設null,故值為0
        System.out.println(System.identityHashCode(t));// 1554874502
        
    }
}
// 重寫後是根據物件中的Field欄位計算出的雜湊值

2、String中的equals和hashCode

2.1.String中hashCode方法

​ String的HashCode()是通過h=31*h+val[i];迴圈計算字元的Ascii的值,最後得到的雜湊值

private int hash; // Default to 0
private final char value[]; //這是字串的字元陣列

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的HashCode是通過h=31*h+val[i];迴圈計算字元的Ascii的值,最後得到一個隨機數,稱為雜湊值,計算方式如下:
// "a":97
// "aA":97*31+65=3072
// "aAa":(97*31+65)*31+97=95329

2.2.String中的equals方法

​ String中的equals()方法目的是比較兩個物件的值是否相等



public boolean equals(Object anObject) {
    if (this == anObject) { // 若地址值相等,則直接返回true
        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;
}

3.HashSet保證元素唯一原理

HashSet底層是雜湊表結構,雜湊表保證元素唯一依賴於hashCode()和equals方法().

  1. 當HashSet集合儲存元素的時候,就會呼叫該元素的hashCode()方法計算雜湊值。

  2. 判斷該雜湊值對應的位置上,是否有相同雜湊值的元素。

  3. 如果該雜湊值對應的位置上沒有相同雜湊值的元素,那麼就直接儲存。

  4. 如果該雜湊值對應的位置上有相同雜湊值的元素,那麼就產生了雜湊衝突。

  5. 如果產生了雜湊衝突,就得呼叫該元素的equals()方法與該雜湊值位置上的所有元素進行一一比較:
    如果該雜湊值位置上有任意一個元素與該元素相等,那麼就不儲存
    如果該雜湊值位置上所有元素與該元素都不相等,那麼就直接儲存

HashSet在JDK1.8之前是由雜湊表+陣列+連結串列實現,1.8之後是由雜湊表+陣列+紅黑樹實現。
HashSet集合元素儲存方式

// HashSet的put方法中,根據雜湊值來判斷陣列索引的位置
if ((p = tab[i = (n - 1) & hash]) == null)//(n-1)&hash等效於hash % n,轉換為陣列索引
    tab[i] = newNode(hash, key, value, null);//此位置沒有元素,直接儲存

在這個陣列的每個索引上雜湊值是不等的,每個連結串列中元素雜湊值可能相等,這樣插入資料就可以減少比較次數。

3.總結

1.hashCode是根據物件的Field欄位來計算出一個int型別的值,即雜湊值

2.equals是用來比較兩個物件是否相等,若兩個物件的型別,欄位都相等則相等

這是兩個概念。

  • 兩個物件equals相等,則說明兩個物件的Field欄位相等,那麼根據Field計算出來hashCode的也相等。【前提是沒有重寫這兩個方法,或者同時重寫了這兩個方法】

  • 兩個物件hashCode相等,則這兩個物件equals相等。【根據hashCode的計算方式就知道不能保證相等的】

3.hashCode()最重要的目的就是:無論何時,對同一個物件呼叫hashCode()都應該產生同樣的值(參考連結中大佬的文章寫的很清晰

參考:http://www.cnblogs.com/dolphin0520/

相關文章