hashCode方法介紹

hashCode方法介紹

hashCode方法的意義

JDK官方API給出的解釋:

public int hashCode()

Returns a hash code value for the object. This method is supported for the benefit of hash tables such as those provided by HashMap.

The general contract of hashCode is:

  • Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer, provided no information used in equals comparisons on the object is modified. This integer need not remain consistent from one execution of an application to another execution of the same application.
  • If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.
  • It is not required that if two objects are unequal according to the equals(java.lang.Object) method, then calling the hashCode method on each of the two objects must produce distinct integer results. However, the programmer should be aware that producing distinct integer results for unequal objects may improve the performance of hash tables.

As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the Java™ programming language.)

大致意思是,hashCode方法用來返回一個整數值,並且這個方法主要爲java中的hash表提供便利,比如像HashMap。

關於hashCode方法有以下幾個約定:

  1. 對於同一個對象,調用他的hashCode()方法必須返回同一個int值。
  2. 如果object1.equals(object2)的結果爲true,那麼object1.hashCode() = object1.hashCode()必須成立
  3. 如果object1.equals(object2)的結果爲false,那麼object1.hashCode() = object1.hashCode()不用必須成立,但是爲不同對象生成不同的hashCode值可能會提高hash表的性能。

常見類對hashCode方法的實現

hashCode方法爲Object類的可繼承方法,默認實現是返回對象的引用,在String和Integer等類型都對hashCode方法進行了重寫。

/**
 * String的hashCode方法  s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
 * s:給定的字符串
 * n:字符創的長度
 * 31: 31 = 2^5 - 1 --> 31 * i = 2 << 5 * i - i --> 位移運算比較快 
 *     31是一個大小合適的質數,算出來的hash值分佈均勻,不容易衝突,太大會導致算出來的int值超出int的範圍
 */
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;
}
// Integer的hashCode方法  int類型直接返回int類型
public static int hashCode(int value) {
    return value;
}

怎麼理解hashCode是爲HashMap提供的

HashMap的結構

在這裏插入圖片描述

HashMap插入數據的過程

HashMap是java對Hash表的一種實現,他保持了哈希表存儲key-value鍵值對的特點,並且可以通過key快速獲取value。他的插入過程大概分爲以下幾個步驟:

  1. 開闢一個數組空間,當插入一個元素時,首先通過hashCode(key)這個方法獲取一個hash值,假如是0,那麼這個元素就被插入到上面圖例中的0這個位置。
  2. 假設再進來一個元素key1-value1,通過我們設計的hashCode(key1)求得的hash值和hashCode(key)相等(哈希衝突),這個時候0這個位置已經有元素了,怎麼辦呢,那就在0這個位置加一個鏈表,將新來的第二個元素存入這個鏈表(鏈地址法)
  3. 當這個鏈表數據越來越多,多到一定程度時候,鏈表這種數據結構的缺點就暴露出來了,雖然向鏈表頭部添加節點的時間複雜度爲O(1),但是查詢的效率是O(N),會導致HashMap的性能出現問題。所以JDK1.8之後,引入了紅黑樹。紅黑樹是一種高度平衡的二叉查找樹,他的查找和插入的時間複雜度都是O(logN),可以很好的解決性能問題。那麼爲什麼不一開始就用紅黑樹,而是當數據到達一定程度時候再用紅黑樹呢,大概是因爲紅黑樹雖然時間複雜度很低,但是爲了達到紅黑樹的平衡,經常需要進行一系列的左旋右旋,也會花費時間。所以其實數據不多的時候用鏈表是比較好的。

爲什麼重寫了equals()方法,一定要重寫hashCode()方法

hashCode()、 ==、 equals()的區別

==比較兩個對象的引用,

hashCode()返回對象的哈希值

equals()的默認實現如下,是比較兩個對象的引用是否一樣,和==是一樣的。

public boolean equals(Object obj) {
    return (this == obj);
}

重寫hashCode()方法提高效率

如果重寫了hashCode()方法,當我們需要比較兩個對象是否相等時,首先計算hashCode,如果不同,則認爲兩個對象不同,不用再用equals()方法進行比較。如果hashCode相同,在通過equals()比較,這樣有利於提高效率。

重寫equals但不重寫hashCode()會發生什麼?

equals()默認實現是比較兩個對象的引用是否一樣。但是大多數情況下我們創建的對象,例如Student對象,我們一般會重寫equals()方法,當滿足年齡、學號、班級等對象都一致的時候才說明他們是同一個對象。這個時候如果不重寫equals()方法,則會出現hashCode算出來的值不同,但是equals()方法比較的結果一致。

hashCode()進一步說明

有了上面的談論,我們在對hashCode()方法做點總結:hashCode的作用

  1. hashCode()函數主要作用是提供一個好的尋址能力,可以快速支持通過key得到value
  2. 另一個作用是和equals結合比較兩個對象是否相等

hashCode方法的要求:

  1. 一致:key1 = key2,則hashCode(key1) = hashCode(key1)
  2. 高效:計算起來要快
  3. 均勻:算出來的hash值要分佈均勻
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章