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

简介: 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);
}

image.gif

设计理念

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

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

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

public static int hashCode(long value) {
return (int)(value ^ (value >>> 32));
}

image.gif

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));

image.gif

// 【2】0 和1 互换

// 0 和1 互换
newValue = (~high32) & (~low32);
System.out.println(newValue);
Assert.assertEquals(Long.hashCode(value), Long.hashCode(newValue));

image.gif

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

java.lang.Long#equals

public boolean equals(Object obj) {
  if (obj instanceof Long) {
    return value == ((Long)obj).longValue();
   }
  return false;
}

image.gif

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

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

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

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

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

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

三、总结

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

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

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


相关文章
|
2月前
计算 long long, long double 字节大小
【10月更文挑战第14天】计算 long long, long double 字节大小
40 7
|
2月前
计算 long long, long double 字节大小
【10月更文挑战第13天】计算 long long, long double 字节大小。
36 4
|
7月前
计算long long, long double 字节大小
计算long long, long double 字节大小。
122 3
|
7月前
|
存储 Java
|
7月前
|
JSON JavaScript 前端开发
解决js中Long类型数据在请求与响应过程精度丢失问题(springboot项目中)
解决js中Long类型数据在请求与响应过程精度丢失问题(springboot项目中)
655 0
|
7月前
|
编译器 C语言
c语言中long的作用类型
c语言中long的作用类型
209 0
|
1月前
|
编译器 C#
c# - 运算符<<不能应用于long和long类型的操作数
在C#中,左移运算符的第二个操作数必须是 `int`类型,因此需要将 `long`类型的位移计数显式转换为 `int`类型。这种转换需要注意数据丢失和负值处理的问题。通过本文的详细说明和示例代码,相信可以帮助你在实际开发中正确使用左移运算符。
38 3
|
1月前
|
编译器 C#
c# - 运算符<<不能应用于long和long类型的操作数
在C#中,左移运算符的第二个操作数必须是 `int`类型,因此需要将 `long`类型的位移计数显式转换为 `int`类型。这种转换需要注意数据丢失和负值处理的问题。通过本文的详细说明和示例代码,相信可以帮助你在实际开发中正确使用左移运算符。
63 1
|
1月前
|
编译器 C#
c# - 运算符<<不能应用于long和long类型的操作数
在C#中,左移运算符的第二个操作数必须是 `int`类型,因此需要将 `long`类型的位移计数显式转换为 `int`类型。这种转换需要注意数据丢失和负值处理的问题。通过本文的详细说明和示例代码,相信可以帮助你在实际开发中正确使用左移运算符。
18 0
|
4月前
|
前端开发 Java 数据库
Java系列之 Long类型返回前端精度丢失
这篇文章讨论了Java后端实体类中Long类型数据在传递给前端时出现的精度丢失问题,并提供了通过在实体类字段上添加`@JsonSerialize(using = ToStringSerializer.class)`注解来确保精度的解决方法。