一、背景
《阿里巴巴 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 和1 互换 newValue = (~high32) & (~low32); 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班孤尽大佬的一些问题,让我也养成了读源码得习惯。
希望大家也可以带着问题去学习,平时开发时偶尔去源码里看一下。