深入浅出聊聊我对JAVA源码中hashCode()与equals()的理解(一)

  一段时间没写文章了,昨天在回忆知识点时突然发现自己对hashCode()理解不到位。在经过一番折腾后,总算有了一点收获,首先由于自己的C++底子不好,在学习过程还是有很多问题(看不懂最底层的源码),所以要感谢一个大佬解答我的疑惑,附上大佬的主页:大佬主页

下面从最简单的开始聊起:

1、首先什么是hash?什么hashCode?

hash是一种函数,要真正去了解hash函数建议去学习数据结构中的查找技术。查找技术有搜索查找和计算查找(函数)等,搜索查找最简单的就是逐个比对,最后找到我们需要的对象所在的位置;而计算查找不需要通过比对(这里说的不需要比对是在没产生hash碰撞最理想的情况下),就是利用函数关系式计算就能找到我们要找元素的位置。实现的机制也不难,如:

假设有一个数组a有4个空间,编号为0、1、2、3。


                              
 现在假设:有一个对象B,分配到的物理地址为6;所用到的计算查找函数为:index=XmodArray.size;   其中index即为计算后得到在数组中的下标,X为对象的物理地址,mod是求余,Array.size是数组大小。那么在将对象B存放到数组a前,经过计算得到应该存放在数组a的下标位置是2,即存放在编号为2的位置上:

 那么下次需要找B时只需用原来的函数:index=XmodArray.size  通过计算就可以得到对象B存放在数组a中的下标,时间复杂度为O(1)。

当然这只是最简单的举例,在实际设计时要考虑许多因素。如:数组空间大小多大合适?采用什么函数才能使对象放在数组中更均匀?等等,主要为减少hash碰撞。

上面所提到的函数即是hash函数,计算所得到的值即为hashCode。当然,这里只是举例,具体的hash函数可以根据需求去设计或选择,比较常用的几个hash函数有以下几种分类:

(1)加法Hash ;
(2)位运算Hash ;
(3) 乘法Hash ;
(4) 除法Hash ;
(5) 查表Hash ;
(6) 混合Hash ;

 2、聊完了hash和hsahCode,下面聊聊JAVA中Object的hashCode。

不知道各位大佬有没有犯过错误,在了解Object源码之前,本人是有一个误区的,即理所当然的认为hashCode()方法返回的是这个对象的物理地址。直到了解源码后:(因为这个是native方法,是C++的代码)

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) {
     //这与第5个方式类似,都调用了cast_from_oop函数,只不过此处又增加了位偏移和addrBits ^(addrBits >> 5) ^ GVars.stwRandom计算,与物理地址无关
     // 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 = cast_from_oop<intptr_t>(obj) >> 3 ;
     value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ;
  } else
  if (hashCode == 2) {
      //任何对象调用,返回的hashCode都为1,与物理地址无关
     value = 1 ;            // for sensitivity testing
  } else
  if (hashCode == 3) {
      //返回一个自增的hashCode
     value = ++GVars.hcSequence ;
  } else
  if (hashCode == 4) {
     //返回物理地址
     value = cast_from_oop<intptr_t>(obj) ;
  } else { 
     // 通过计算,返回一个伪随机数(这是默认的返回hashCode的方式,与物理地址无关)
     // 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;
}

可以看到,其实hashCode()方法中有5中返回hahsCode的方式,而默认的是最后一种,即最后else那部分。这返回的是一个伪随机数,与物理地址无关,只有当传入的这个函数内部的变量hashCode等于4时,返回的才是对象的物理地址。

那么如何才能使的函数内部的变量hashCode等于4呢?这其实是JVM的一个启动参数,只要在启动是传入就可以:

(1)

(2) 

(3) 

 以上就是JAVA中Object类的hashCode产生的过程与如何选择不同的方式。

由于时间关系,今天先不写equals()方法相关的知识了,详见下一篇更新的内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值