Java hashCode() 方法深刻理解

Java.lang.Object 有一個hashCode()和一個equals()方法,這兩個方法在軟件設計中扮演着舉足輕重的角色。在一些類中覆寫這兩個方法以完成某些重要功能。本文描述了爲何要用hashCode(), 如何使用,以及其餘的一些擴展。閱讀本文須要有基本的hash算法知識以及基本的Java集合知識,本文屬於菜鳥入門級講解,大神讀至此請點擊右上角的X,以避免浪費您的時間^_^。java

WHY hashCode()?算法

集合Set中的元素是無序不可重複的,那判斷兩個元素是否重複的依據是什麼呢? 「比較對象是否相等固然用Object.equal()了」,某猿如是說。可是,Set中存在大量對象,後添加到集合Set中的對象元素比較次數會逐漸增多,大大下降了程序運行效率。 Java中採用哈希算法(也叫散列算法)來解決這個問題,將對象(或數據)依特定算法直接映射到一個地址上,對象的存取效率大大提升。這樣一來,當含有海量元素的集合Set須要添加某元素(對象)時,先調用這個元素的hashCode(),就能一會兒定位到此元素實際存儲位置,若是這個位置沒有元素,說明此對象時第一次存儲到集合Set, 直接將此對象存儲在此位置上;若此位置有對象存在,調用equal()看看這兩個對象是否相等,相等就捨棄此元素不存,不等則散列到其餘地址。 HOW use hashCode()?jvm

Java語言對猿設計equal()有五個必須遵循的要求。ide

對稱性。若 a.equal(b) 返回」true」, 則 b.equal(a) 也必須返回 「true」. 反射性。a.equal(a) 必須返回」true」. 傳遞性。若a.equal(b) 返回 「true」, 且 b.equal(c)返回 「true」, 則c.equal(a)必返回」true」. 一致性。若a.equal(b) 返回」true」, 只要a, b內容不變,無論重複多少次a.equal(b)必須返回」true」. 任何狀況下,a.equals(null),永遠返回是「false」;a.equals(和a不一樣類型的對象)永遠返回是「false」. hashCode()的返回值和equals()的關係.函數

若是a.equals(b)返回「true」,那麼a和b的hashCode()必須相等。 若是a.equals(b)返回「false」,那麼a和b的hashCode()有可能相等,也有可能不等。 下面是一個例子。在實際的軟件開發中,最好重寫這兩個方法。性能

public class Employee {
    int        employeeId;
    String     name;

    // other methods would be in here 

    @Override
    public boolean equals(Object obj)
    {
        if(obj==this)
            return true;
        Employee emp=(Employee)obj;
        if(employeeId.equals(emp.getEmployeeId()) && name==emp.getName())
            return true;
        return false;
    }

    @Override
    public int hashCode() {
        int hash = 1;
        hash = hash * 17 + employeeId;
        hash = hash * 31 + name.hashCode();
        return hash;
    }
}

下面着重介紹一下經常使用類的hashCode()實現方法。優化

String類的hasCode()this

Java代碼設計

public int hashCode() {
    int h = hash;
    if (h == 0) {
        int off = offset;
        char val[] = value;
        int len = count;

            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];
            }
            hash = h;
        }
        return h;
    }

這段代碼最有意思的仍是hash的實現方法了。最終計算的hash值爲: s[0]31n-1 + s[1]31n-2 + … + s[n-1]指針

s[i]是string的第i個字符,n是String的長度。那爲何這裏用31,而不是其它數呢?

31是個奇素數,若是乘數是偶數,而且乘法溢出的話,信息就會丟失,由於與2相乘等價於移位運算。使用素數的好處並非很明顯,可是習慣上都使用素數來計算散列結果。31有個很好的特性,就是用移位和減法來代替乘法,能夠獲得更好的性能:31*i==(i<<5)-i。如今的VM能夠自動完成這種優化。(From Effective Java)

Object類的hasCode()

Object類中hashCode()是一個Native方法。Native方法如何調用?

public native int hashCode();

Object類的Native方法類可在這裏找到。 深刻分析請看另一篇博客

static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

源代碼包括getClass()(See line58)等, hashCode()(See line43)被定義爲一個指向JVM_IHashCode指針。

jvm.cpp中定義了JVM_IHashCode(line 504)函數, 此函數裏調用ObjectSynchronizer::FastHashCode,其定在 synchronizer.cpp, 可參考576行的FastHashCode 和 530行的 get_next_hash 的實現。

相關文章
相關標籤/搜索