问二:谈谈你对HashMap的理解?

HashMap(Java8以前):数组+链表

HashMap(Java8及以后):数组+链表+红黑树

 

 

如果存入的key是一个自定义的类,该怎么办呢?(重新equals和hashcode)

进入源码:

可以看到key是由Set来存的,就不能够有重复数据了

values用Collection来存,可以重复

内部存储结构是[是一个MapEntry的实现]

数组被分为一个个的bucket,通过hash值决定了键值对在这个数组的寻址,hash值相同的键值对则以链表的方式存储

 

如果链表的大小超过了一定数值,默认8,则会转化为红黑树

当链表的大小小于一定数值,默认6,则会由红黑树转换回链表

再看HashMap 的构造函数,可以发现它仅仅只是为一些相关的成员变量赋上了初始化值,所以可以大胆猜测:HashMap的加载是懒加载(lazy-load的模式)

接下来看它的put方法

调用putVal,点进去,发现最初几行调用resize()方法

然后回到putVal方法,继续分析,这一步是得到经过hash之后的值来确定位置,如果Node<K,V>[] tab这里面没有,就新建一个,放进去

如果发现在tab[i]中存在键值对,并且key和传进来的key相同,就直接替换数组中的元素

然后再判断是否是树化后的节点,如果是的话,就按照树的方式尝试存储键值对进行存储

如果都不是,那么就直接在链表后面新建一个,但是如果binCount超过了阈值,那么就进行树化

 

总结HashMap的put方法逻辑:

1.如果HashMap未被初始化过,则初始化(也就是进行resize()操作)

2.对key进行一个hash操作,并且和数组长度减一后进行与操作,计算出下标

3.如果没有碰撞,就直接放入桶中

4.如果碰撞了,就以链表的方式链接到后面(拉链法

5.如果链表长度超过阈值,就把链表转成红黑树

6.如果链表长度低于6,就把红黑树转回链表

7.如果节点已经存在,就替换旧值

8.如果桶满了容量16*加载因子0.75),就需要resize扩容2倍后重排

 

HashMap如何有效减少碰撞?

扰动函数:促使位置分布均匀,减少碰撞几率

使用final对象,并且采用合适的equals()和hashCode()方法

 

HashMap中hash值的求法

先取到key的hashCode,再在其基础上进行移位,将高16位移动到低16位上面,再与原先的数据进行异或运算

这样做的好处是什么?

第二步能够综合高16位和低16位的数据情况,以此加大低位的随机性,而且低位也相当于变相保存了高位信息。主要是从速度、质量、功效来考虑的

第三步中,(n - 1)相当于是桶(或者数组)的长度【?】,这一步的运算就相当于是用hash值与长度取模,但是这个效率更加快

[n–1的原因是:因为初始状态是2的n次方,如果减一个1的话,它的位数就正好表示n钟情况,比如16-1就15,15的话就是0000 1111,有4位,2的4次方,就是16钟情况了,刚好可以把他们放到16个桶里面去]

更快的原因是由于hashMap的长度是2的n次方,那为什么是2的n次方而不是传入的长度呢?

其实在传入初始化capacity(容量)值的时候,还额外调用了一个方法:tableSizeFor

它的作用其实就相当于是将传入的参数转换成离它最近的2的倍数的值

为了方便在进行hash运算定位桶的时候能实现用上述的与操作,代替取模,从而获得更好的效果.

这儿相当于一道非常漂亮的算法题:输入一个int类型的数,返回比某个2的倍数大且最接近的那个数

附: 

按位或后赋值   |=   

无符号右移     >>>

 

为什么不直接用获取到的key.hashCode()来访问数组呢?

因为hashCode()方法返回的是int类型,而其范围是-2147483648~+2147483647(即正负2的16次方),然而一个40亿长的数组,内存是不容易存储的,更何况默认的hashMap在扩容之前大小才16,所以直接拿散列值来用是不现实的

 

HashMap是如何扩容的?

首先了解一个变量DEFAULT_LOAD_FACTOR(默认负载因子),意思是当一个HashMap填满了75%的bucket时,就会自动扩容

threshold 阈值 

原理是通过创建原来HashMap大小x2的bucket数组,来重新调整map的大小

并将原来的对象放入到新的bucket数组中去(这个过程叫做rehashing,这个算法挺有意思的,挖个坑,有心情就来填)

 

HashMap扩容会存在哪些问题?

多线程环境下,调整大小会存在条件竞争,容易造成死锁

rehashing是一个比较耗时的过程

 

如何让HashMap转为线程安全?

可以通过

让hashMap线程安全。其原因是添加了一个互斥对象成员,对里面的public方法使用synchronized进行加锁

效果上和HashTable差不多

为什么HashMap线程不安全?

可能造成Bucket中链表形成环形链表,导致后续的Map.get操作可能会造成无限循环,导致CPU的100%占用。

 

 

全部评论

相关推荐

01-12 14:08
门头沟学院 Java
有寒假来武汉小米总部实习的大学生嘛,我也是小米的员工,想找合租舍友,仅限女生可免租半月,二月初可入住,也就是说房租是2.15开始算的哦~也可以将行李提前放过来~房屋介绍:1、房子情况:有电梯;租的是三室一厅一卫一厨,&nbsp;但是有个卧室比较小,不打算找人,只住两个人就可以了;衣柜也很大,可以放下很多衣服;房屋采光真的很好,早上起来可以在床上晒太阳的那种,十分惬意(夏季晚上十分好看!)2.&nbsp;楼下离我们很近的地方有小吃街和一个两层大超市(大概步行两分钟多就可以走到)&nbsp;,还有一个新开的麦当劳,晚上可以去吃小吃,购买物资也可以去大超市;3.&nbsp;房子基本设施齐备(洗衣机,冰箱,空调,油烟机,热水器);4.&nbsp;我有稳定的工作,生活中很注意卫生,周末有时间会自己做饭,可以投喂哦~5.&nbsp;出行:距离公交站步行10分钟不到,距政务中心,武汉小米总部三站(晚上我都是走回来的,很近的~);一个比较进的地铁,距离大概1km左右;出入我觉得很方便;6.&nbsp;房租:1150每月,押一付二,无物业费,也没有中介费和其他额外费用。7.&nbsp;民用水电燃气,用多少交多少,水电费正常平摊。希望你是:1.&nbsp;女生(本人女),不带异性回家,如有同性朋友来玩,最多过夜一晚;2.&nbsp;爱干净,讲卫生,作息正常,不吵闹,有稳定工作;3.&nbsp;好沟通,有任何问题一定要沟通,不要闷着!中介勿扰,非诚勿扰!!!希望不要浪费彼此的时间诚心有意向的可以联系我看房
租房找室友
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务