Object-hashCode演算法筆記

伊阿·珀懷特發表於2020-12-18

hashCode()是什麼

Object的hashCode()通過JVM虛擬機器來實現其演算法,主要為物件計算出一個hash值

hashCode()的作用及怎麼用:
通過該hash值可以高效的對兩個物件進行比較,比較邏輯如下:

	if (A.hash != B.hash){
        A != B
    }else {
        if (A.equals != B.equals) {
            A != B
        } else {
            A == B
        }
    }

輔助理解:
Object的hashCode演算法會被很多基礎類使用,比如HashMap中計算hash值,但在跟蹤jdk 1.8原始碼時發現hashCode定義是native,所有就需要去扣hashCode原始碼(Object中hashCode的宣告:public native int hashCode();)

在扣原始碼之前有些朋友誤以為預設情況下,hashCode返回的就是物件的儲存地址,事實上這種看法是不全面的,確實有些JVM在實現時是直接返回物件的儲存地址,但是大多時候並不是這樣,只能說可能儲存地址有一定關聯。下面是HotSpot JVM中生成hash雜湊值的實現:

static inline intptr_t get_next_hash(Thread * Self, oop obj) {
  intptr_t value = 0 ;
  if (hashCode == 0) {
     // This form uses an unguarded global Park-Miller RNG,
     // so it's possible for two threads to race and generate the same RNG.
     // On MP system we'll have lots of RW access to a global, so the
     // mechanism induces lots of coherency traffic.
     value = os::random() ;
  } else
  if (hashCode == 1) {
     // This variation has the property of being stable (idempotent)
     // between STW operations.  This can be useful in some of the 1-0
     // synchronization schemes.
     intptr_t addrBits = intptr_t(obj) >> 3 ;
     value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
  } else
  if (hashCode == 2) {
     value = 1 ;            // for sensitivity testing
  } else
  if (hashCode == 3) {
     value = ++GVars.hcSequence ;
  } else
  if (hashCode == 4) {
     value = intptr_t(obj) ;
  } else {
     // Marsaglia's xor-shift scheme with thread-specific state
     // This is probably the best overall implementation -- we'll
     // likely make this the default in future releases.
     unsigned t = Self->_hashStateX ;
     t ^= (t << 11) ;
     Self->_hashStateX = Self->_hashStateY ;
     Self->_hashStateY = Self->_hashStateZ ;
     Self->_hashStateZ = Self->_hashStateW ;
     unsigned v = Self->_hashStateW ;
     v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ;
     Self->_hashStateW = v ;
     value = v ;
  }
 
  value &= markOopDesc::hash_mask;
  if (value == 0) value = 0xBAD ;
  assert (value != markOopDesc::no_hash, "invariant") ;
  TEVENT (hashCode: GENERATE) ;
  return value;
}

該實現位於hotspot/src/share/vm/runtime/synchronizer.cpp檔案下。如上參考:淺談Java中的hashcode方法

在有些情況下,程式設計者在設計一個類的時候為需要重寫equals方法,比如String類,但是千萬要注意,在重寫equals方法的同時,必須重寫hashCode方法。為什麼這麼說呢?

主要是Object原生的hash值可以理解為取的是物件地址,如果比較兩個String的話,兩個物件的地址一定不同,如果不重寫hashCode兩個物件一定不相同(相同的意思是內容相同,而不是同一個物件),故為了達到時兩個物件內容相同的情況下hashCode相關,則需重寫Object.hashCode();且保證只要後設資料相同,則hashCode必須相同,不相同的後設資料hashCode也可以相同

hash主要提供了一種快速查詢的手段,但該手段同時會引入衝突問題,如何解決衝突問題的方法則將在後續闡述

相關文章