关于Java hashCode方法中位运算及组合逻辑的技术疑问
关于hashCode方法中位运算及组合操作的疑问解答
问题描述
我已编写如下hashCode方法与equals方法,其中equals方法的逻辑已理解,但无法理解hashCode方法中的id ^ (id >>> 32)等位运算及后续的组合操作,想请教执行这些操作的原因。附代码如下:
public int hashCode(){ int res = (int) (id ^ (id >>> 32)); res = 31 * res + (name != null ? name.hashCode() : 0); return res; }
public boolean equals(Object o) { // object ref identical or not if (this == o) return true; if (o == null) return false; //class identical? if (getClass() != o.getClass()) return false; Person person = (Person) o; if(id != person.id) return false; return Objects.equals(name, person.name); }
我了解equals与hashCode存在关联,因此附上equals代码供参考。
解答
1. id ^ (id >>> 32)的作用
这里可以推断你的id是long类型(int只有32位,无符号右移32位后会变成0,这个运算就失去意义了)。这行代码的核心是在将64位的long值压缩为32位int时,尽可能保留原数值的全部位信息:
id >>> 32:将long类型的id做无符号右移32位,把原数的高32位移到低32位的位置^(异或运算):把原id的低32位和移位后的低32位(原高32位)做异或运算,这样最终得到的int值同时融合了原long值高、低32位的特征。
如果直接强转(int)id,只会保留低32位,高32位的信息完全丢失——如果两个long类型id的低32位相同但高32位不同,强转后得到的int值会完全一样,大幅提升哈希冲突的概率。而异或操作能让高32位的差异也体现在最终哈希值里,有效降低冲突。
2. 31 * res + (name != null ? name.hashCode() : 0)的逻辑
这部分是按照Java哈希码的通用设计规则,组合多字段的哈希值:
- 选择31作为乘数:31是质数,同时二进制为
11111,JVM可将其优化为(res << 5) - res,运算效率极高;另外质数与其他数相乘的结果重复概率更低,能减少不同字段组合后哈希碰撞的可能性。 - 加入name的哈希值:由于你的equals方法同时比较了
id和name,根据Java规范——equals返回true的两个对象,hashCode必须相等——所以hashCode必须包含equals中用到的所有字段,才能保证逻辑一致性,避免HashMap等哈希容器出现错误。 - 空值处理:当name为null时用0代替哈希值,避免抛出NullPointerException。
3. 与equals逻辑的匹配性
你的equals方法判断两个Person对象相等的条件是:同一引用、非空、同类型、id相等、name相等。对应的hashCode方法结合id和name的哈希特征,正好满足:
- 若两个对象equals为true,它们的id和name完全相同,哈希值必然相等
- 若两个对象equals为false,要么id不同,要么name不同,哈希值大概率不同(最大化降低冲突概率)
内容的提问来源于stack exchange,提问作者AishaWho
相关产品推荐
相关产品推荐

