相关历史文章(阅读本文之前,您可能需要先看下之前的系列)
色谈Java序列化:女孩子慎入 - 第280篇
烦不烦,别再问我时间复杂度了:这次不色,女孩子进来吧 - 第281篇
双向链表,比西天还远?- 第282篇
面试不再怕,让LRU无处可逃 - 第283篇
爱我,就要懂我 – Memcached- 第284篇
内存管理,难于上青天?- memcached - 第285篇
你懂她,可惜你不懂我「LRU 」- Memcached- 第286篇
分布式算法真是吊炸天 – memcached- Memcached - 第287篇
探索内存碎片化 - 第288篇
Bb你还给老师了吗?- 第289篇
进制8421,这么妖?- 第290篇
师傅:徒儿,你看看如下的应该会是怎么结果?
JAVA Code:
String str1 = "Aa";
String str2 = "BB";
System.out.println(str1.equals(str2));
System.out.println(str1.hashCode() == str2.hashCode());
上面的打印结果是:
A :true,true
B :true,false
C:false,true
D:false,false
悟纤:equals肯定是false了,不同的字符串值比较肯定不能相等了。那主要是第二个结果的输出了,我记得之前看到一些资料说hashCode()的结果是【通过对象的内存地址做相关运算得到的】,如果是这这样子的胡啊,那么这两个明显是不同的对象,应该是不会相等吧。据此分析答案应该是D。
师傅:那徒儿你今天有必要认真听一下这一课了。
一、常见类型的hashCode
师傅:来,徒儿,为师先给你看一个漂亮的美女,不,是展示一段酷炫的代码。
JAVA Code:
Integer a = 10;
System.out.println(a.hashCode());//10
String str = "A";
System.out.println(str.hashCode());//65
Date date = new Date();
System.out.println(date.hashCode());//-976369987
String ss = new String("123");
System.out.println(ss.hashCode());//48690
MeiMei meimei = new MeiMei();
System.out.println(meimei.hashCode());//2018699554
打印输出结果:
10
65
-975968356
48690
2018699554
预想:
1)、Integer类型的数据,返回的hash值为数据本身;
2)、对象类型的数据,返回的一串字符;
3)、String类型的数据,返回一串字符;
要看我们的预想的正确性需要了解下hashCode是何女人,让美女带你装逼带飞升^_^。
二、Object.hashCode()竟这么拽?
师傅:来打开你的java.lang.Object。
八戒:怎么打开呢?
悟纤:不怕虎一样的敌人,就怕猪一样的队友。
悟纤:随便一个开发工具都能查看源码。
悟纤:哇卡哇卡,啥都没有呢??
public native int hashCode();
师傅:不要大惊小怪,native就是本地方法调用,搞得没见过女人一样。
悟纤:师傅,徒儿一直和你在一起,哪有时间约美眉呐。
在Java中native标记的方法就是本地方法调用,使用JNI技术调用本地C++代码编写的方法。
这里就不展开分析c++中的hashCode,感兴趣的可以看一下这一篇文章:
https://www.jianshu.com/p/be943b4958f4。
这里看一下文章的结论:
OpenJDK8 默认hashCode的计算方法是通过和当前线程有关的一个随机数+三个确定值,运用Marsaglia's xorshift scheme随机数算法得到的一个随机数。和对象内存地址无关。
三、Integer.hashCode()返回的hash为数据本身?
Integer.hashCode是否是返回的本身,这个就可以使用源码进行查看了,Integer这个类重写了hashCode方法:
public static int hashCode(int value) {
return value;
}
可以看到就是返回了value值本身。
四、String.hashCode()又是什么?
查看String.hashCode源码:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
这段代码如何理解:
(1)int h = hash:hash是全局变量,默认值为0,这个主要是为了不用重复计算,二次调用就直接返回h的值了。
(2)if(h==0 && value.length>0) : h不为0,并且string的值的长度大于0的,执行hash值的计算。所以这里得出String s =""的hashCode的值应该为0。
(3) for循环:在for循环中就是遍历字符串中的字符。这里的val[i]是字符串对应的ASCII。(A= 65,a = 97)。
For循环的公式就是:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
这里为什么选择31这个数呐?
首先质数31作为参数进行计算,哈希值不容易冲突,分布比较均匀,且没有超出int范围。再次,31可以被jvm优化,i*31=(1<<5)-i,即进行左移位运算,快很多。
我们来手动计算一下"AB"的hashCode是多少?
计算过程为:
(1)、(31*0+'A') = 65
(2)、(31*0+'A')*31+'B' = 65*31+66 = 2081
通过代码测试得到的结果也是2081。
到这里,我们一开始提出的问题"Aa"和"BB"的hashcode是否相等,那么就是他们这个计算结果是否相等了。
hashCode(Aa) = (31*0+'A')*31+'a'= 65*31+97 = 2112。
hashCode(BB) = (31*0+'B')*31+'B'
= 66*31 + 66
= (65+1)*31+66
= 65*31+31+66=65*31+97 = 2112。
好了,本篇介绍就到此为止了,至于date为什么是负数的值呐,源码自己去看看,就知其所以然了。
悟纤:师傅,你这一波操作,好骚呀,让我都不能懂装不懂了。
师傅:徒儿,你还得好好修炼修炼。
悟纤:徒儿,遵命,这就去修炼修炼。
我就是我,是颜色不一样的烟火。
我就是我,是与众不同的小苹果。
学院中有Spring Boot相关的课程:
à悟空学院:https://t.cn/Rg3fKJD
SpringBoot视频:http://t.cn/A6ZagYTi
Spring Cloud视频:http://t.cn/A6ZagxSR
SpringBoot Shiro视频:http://t.cn/A6Zag7IV
SpringBoot交流平台:https://t.cn/R3QDhU0
SpringData和JPA视频:http://t.cn/A6Zad1OH
SpringSecurity5.0视频:http://t.cn/A6ZadMBe
Sharding-JDBC分库分表实战:http://t.cn/A6ZarrqS
分布式事务解决方案「手写代码」:http://t.cn/A6ZaBnIr