漫谈HashMap

一直想在csdn上开个博客,写一些Java生态圈的技术类的文章,在与广大程序猿朋友们交流的同时也记录下自己学习的过程和结果,也记录下曾经奋斗的日子与青春。

万事开头难,第一篇文章开始的特别艰难,不知道从哪儿开始该写些什么,正好最近有面杭州某互联网公司,这里起个代号叫A吧,和面试官聊得很开心,不得不夸下,也接触过很多公司,但是A公司的面试官真的很有素质,不管面试者水平如何基本都能聊得心里舒服,有的公司的面试官让人很反感,觉得自己高高在上牛逼哄哄的,殊不知换个位子,可能还不如被面试的人,满瓶不动半瓶摇,扯远了,A公司面试官问了一个问题“为什么HashMap的key最好常用String类型的?”,这个问题其实我之前没有看到过,就根据自己的理解回答说“因为String是final类型的,对象创建完成后不可变,用它作为key不会产生put进去拿不出来的情况,避免了内存泄露等等”,他笑了下,继续问“有没有看过HashMap的源码,里面的hash函数如何实现的,String类型的有什么特别的?”,源码我是看过,但不是能够记住全部细节,然后就说大概是“根据对象本身的hashCode方法获取值,然后进行一些移位、异或操作,我知道String类型的是特别处理的,但具体怎么处理我就不知道了”,好的扯了这么多,进入正题,HashMap到底对String类型的key做了什么优化呢?

1、直接定位到HashMap的put方法


2、找到hash方法


get it!这里的hashSeed是个什么东东?看看构造函数里有没有初始化的地方,很遗憾,然而并没有我们想看到的东西,



3、还是回到put方法,一开始的时候有个初始化的方法,看看是不是在那个里面


找到了,从方法名基本就可以看出来是初始化hashSeed的


代码的逻辑并不复杂,boolean switching = currentAltHashing ^ useAltHashing;,决定switching是true还是false的是useAltHashing,通过自己debug或是sysout可以知道sun.misc.VM.isBooted()的值是true,所以做最终决策的是capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD,capacity的默认值是16,就看Holder.ALTERNATIVE_HASHING_THRESHOLD是多少了

4、定位到Holder类


这里我分成了5步

(1)定义成常量但是不赋值是有原因的,这里容易忽视,如果这里直接赋值,那么调用的类是直接用的常量值,不会执行Holder类的static静态代码块

(2)altThreshold的值是从jvm环节变量中取的,可以设置启动参数-Djdk.map.althashing.threshold=1

(3)threshold的取值逻辑判断,是从环境变量解析还是使用默认值(Integer的最大值)

(4)特殊值处理、异常处理

(5)设置ALTERNATIVE_HASHING_THRESHOLD值,大功告成


5、简单的测试用例

@Test
public void test7()
{
    System.out.println(sun.misc.VM.isBooted());
    Class clazz= HashMap.class;
    try {
        Method method= clazz.getDeclaredMethod("hash", Object.class);
        method.setAccessible(true);
        Map map= new HashMap();
        final String keya= "a";
        String keyb= "b";

        System.out.println(method.invoke(map, keya));
        System.out.println(method.invoke(map, keyb));
        System.out.println(Hashing.stringHash32(keya));
        System.out.println(Hashing.stringHash32(keyb));

        map.put(keya, "a");
        map.put(keyb, "b");
        System.out.println(method.invoke(map, keya));
        System.out.println(method.invoke(map, keyb));
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
}

执行结果:(第一次)

true
103
100
323026698
723087912
323026698

723087912

执行结果:(第二次)

true
103
100
-1712265842
386311424
-1712265842
386311424


6、小提示

上面我贴了俩次执行结果,细心的童鞋已经发现了,俩次的执行结果有差异,Hashing.stringHash32()方法在同一个JVM环境的执行结果是一样的,所以使用它的时候千万要慎重,我们有时候做key的时候喜欢将复杂字符串信息化,常用的有CRC、MD5,SHA等,这里千万不能用stringHash32,否则很可能下次就取不出来了偷笑


后记

第一次写技术文章,战战兢兢地心情,难免有错误的地方,欢迎大家批评指正,希冀与大家共同进步!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值