哈希值,又称:散列函式(或散列算法,又称哈希函式,英语:Hash Function)是一种从任何一种数据中创建小的数字“指纹”的方法。想要了解哈希值,就需要了解哈希函数的性质。

Java中哈希值的计算以及压缩得到索引

一.计算哈希值
 
1.对于基本数据类型四类八种 byte short int long float double char boolean计算过程,大概可分为六类(其实是我自己分的,我只是为了写着清楚)
 
//第一类 int
       //int类型数据的包装类(因为算hashCode()肯定得是对象嘛,所以包装类)求哈希值,直接返回本身的值
 
Integer integer = 2147483647;
System.out.println(integer.hashCode());
//第二类 byte short char
       //这三种计算哈希值,直接返回int类型的自身值,跟第一类类似或者说一样
 
Byte aByte = 127;
System.out.println(aByte.hashCode());
 
Short aShort = 32767;
System.out.println(aShort.hashCode());
 
Character character = '0';
System.out.println(character.hashCode());
//第三类 float float也是32位数据,调用Float的floatToIntBits(float f),返回当前值对应二进制的int类型的值
 
Float aFloat = 32.1321F;
System.out.println(aFloat.hashCode());
//第四类 long long为64位数据,直接转int(32位)的话会有精度损失,因为高32位直接被截断了,所以低32位相同的数值会返回相同的哈希值,
       //这好吗,这不好(请自动脑补马保国老师形象),所以执行一个叫"折叠"的操作
 
Long aLong = 2147483648L;
//下边这一步就叫做 折叠 folding
//>>>这个叫做无符号右移 32就是右移32位 无符号的意思是右移32位,其余位全都补零
//^这个叫做异或,位不同则为1,相同则为0
int longHashValue = (int) (aLong ^ (aLong >>> 32));//这里的^异或是为了使结果更加"随机"
System.out.println(longHashValue);
System.out.println(aLong.hashCode());
//第五类 double
       //double也是64位数据,有点类似刚才的float和long,只不过把两者合在一起了
       //先调用Double的doubleToLongBits(double d),返回当前值对应二进制的long类型的值
       //然后再对转完的long值进行折叠,整体如下
 
Double aDouble = 412312.123213;
long tempLong = Double.doubleToLongBits(aDouble);
int doubleHashValue = (int) (tempLong ^ (tempLong >>> 32));
System.out.println(doubleHashValue);
System.out.println(aDouble.hashCode());
//第六类 boolean
       //这没啥说的 一共就俩 true返回1231   false返回1237
       //今天刚看到的时候蒙蔽了,为啥返回这俩数,查了下,为了使哈希值求索引时候尽量平均分配,所以选了这俩大素数
       //不过为啥恰好就是这俩呢,有大神的话给解释一下呗
 
Boolean aBoolean = true;
System.out.println(aBoolean.hashCode());
2.对字符串求哈希值
 
上边是对基本数据类型求哈希值,更经常用到的就是对字符串求哈希,下边来说,
对字符串求哈希值,基本的方式就是对字符串内每个字符求一个哈希值,然后加总
但是这样会有个问题,就是字母相同,顺序不同的字符串(如:hello,ohell)会返回相同的哈希值(冲突,碰撞,collision),再一次,这好吗,这不好
所以牛掰的哈希函数考虑了字符串的顺序
 
String string = "tai";
//f(string) = f(t)*b^2 + f(a)*b^1 + f(i)*b^0 (这个^是次方,从b的(字符串长度-1)次方开始,一直到b的0次方结束),b是一个常数,设计者采用了31(目的就是减少哈希冲突)
int stringHashValue = (31*31*('t'))+(31*('a'))+(1*('i'));//ps.这里的计算实际和java源码的计算顺序是不一样的,这里的字符串只有三位,不会出现结果溢出的情况,
//如果位数多了的话可能会出现(不是可能,一定会出现)溢出,所以下边两个值可能不相等,不过这不影响理解
System.out.println(stringHashValue);
System.out.println(string.hashCode());
二.对哈希值进行"压缩",得到索引
 
1.由哈希值到索引
 
上边就是基本类型和字符串类型的计算哈希值的方法,值是计算出来了,后边呢,一般哈希值都是用来求索引的,也就是hashMap hashSet里边用,你懂我意思吧,
但是求出来的哈希值一般都比较大,不太好直接拿来做索引,因为会超出索引范围,假如算出来个哈希值114588,数组一共才16位,怎么做索引,
简单办法就是取余,上边例子,拿114588对16取余,余12,好的,索引就是12了(这个过程叫压缩 compressing)
 
int hashValue = 114588;
int N = 16;
int index = hashValue % N; //index = 12
//这样可以求出来某个哈希值对应的索引,不过还有快速的方法,这个快是指计算机运行的快,也就是位运算
//当上边的 N 为2的整数次幂时,
index = hashValue & (N -1);//与hashValue % N等价,but faster
// & 代表与运算,都为1,结果才为1
三.结尾:
 
至此,就基本上明确了基本数据类型和字符串的哈希值计算,及对应索引计算的过程
不过,hashMap和hashSet里边的源码还进行了一些额外的减小哈希碰撞的操作,不过整体就是这么个意思了
对象的哈希还没看,回头看看再写
 
ps. csdn这排版是真xx难用........
 

哈希世界创造了一个平行世界,只为你们而存在,而你们可以在其中与你们接触。
Copyright © 2022 www.57jianzhi.com 哈希hash论坛 版权所有