Long的哈希值计算理念和哈希冲突的情况举例

一、背景

《阿里巴巴 Java开发手册》的作者孤尽大佬举办的第一期 DIY 班,由最初的几百人,目前仅有60多人坚持到现在。

其一,Deeply Inspire Yourself           深度激发自己

其二,Do It Yourself           实践出真知

其中第 20 期的题目是:自主选择一个类,说明它hashCode方法的设计理念和代码核心逻辑。

本人选取 Long这个类进行分析。

二、分析

2.1 自主选择一个类,说明它hashCode方法的设计理念和代码核心逻辑。

java.lang.Long#hashCode(long)

/**

* Returns a hash code for this {@code Long}. The result is

* the exclusive OR of the two halves of the primitive

* {@code long} value held by this {@code Long}

* object. That is, the hashcode is the value of the expression:

*

* <blockquote>

* {@code (int)(this.longValue()^(this.longValue()>>>32))}

* </blockquote>

*

* @return a hash code value for this object.

*/

@Override

public int hashCode() {

return Long.hashCode(value);

}

设计理念

long 64 位 int 34 位,而 hash值是 int 类型,因此需要将 64位转化为小于等于32位。

让高位和低位都能够参与到 hash 运算中,让结果离散。

核心逻辑:long 的 hash 值是通过将前半段和后半段异或得到的。

public static int hashCode(long value) {

return (int)(value ^ (value >>> 32));

}

2.2 如何构造hash冲突

由于 long 的hash 值是将 64 位运算得到32位,即讲一个大范围映射到小范围,因此必然会有 hash冲突。

根据 Long 的 hash函数映射规则,可以看出最简单的构造方式就是根据 hash 规则,将低位和高位互换或者 0 和1 互换,运算结果一致。

// 【1】高低位互换

long value = Long.MAX_VALUE - 1024L;

System.out.println(Long.toBinaryString(value));

// 取高32位

long high32 = value >>> 32;

// 取高32

long low32 = value << 32;

// 高 32 和 低32 互换

long newValue = high32 | low32;

// 新的hash 值 和原值相同

System.out.println(newValue);

Assert.assertEquals(Long.hashCode(value), Long.hashCode(newValue));

// 【2】0 和1 互换

// 高位取反,低位 0 填充

newValue = Long.MAX_VALUE & value;

System.out.println(newValue);

Assert.assertEquals(Long.hashCode(value), Long.hashCode(newValue));

通过hash 值可以快速判断两个值是否不同,然后再通过 equals 函数判断是否相同。

java.lang.Long#equals

public boolean equals(Object obj) {

  if (obj instanceof Long) {
    return value == ((Long)obj).longValue();
   }

  return false;
}

而且重写equals时要修改 hashcode函数确保 相等的两个对象的hashcode 一定相同。

只有这样才能保证优先通过hashcode判断有可能相(hash值相同)等的情况下再调用equals判断是否相等。

hash函数的本质是将数据编码成数字,而 java 中的 hashcode 返回值是 int 类型,如果hash函数写的不好或者某种那类型的数量大于整数,必然可能产生冲突(不同的对象相同的哈希值)。

通常让每一位都参与到运算中等手段设计更优秀的hash函数,尽可能避免hash冲突。

冲突之后通常需要通过 再散列,拉链,公共溢出去等方式来解决。

同样地,大家也可以选取其他类,如 String, Integer 等,去看他们的hash值的计算方式,研究如何构造hash冲突。

三、总结

很多人开发的时候很少去看源码,导致对很多知识点一知半解。

通过DIY班孤尽大佬的一些问题,让我也养成了读源码得习惯。

希望大家也可以带着问题去学习,平时开发时偶尔去源码里看一下。

发布了379 篇原创文章 · 获赞 862 · 访问量 132万+

猜你喜欢

转载自blog.csdn.net/w605283073/article/details/103001627
今日推荐