HashMap源码分析

来源:互联网 发布:世界经济学家排名 知乎 编辑:程序博客网 时间:2024/06/02 02:12

       基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。

    1. HashMap的初始容量和加载因子的设置。

    理想状态下,在哈希表单位空间内的存储节点符合泊松分布。参数lamada是0.5(在加载因子0.75的阀值下),公式如下:

exp(-0.5) * pow(0.5, k) /factorial(k).

泊松分布是指单位时间或单位空间内发生事情的概率,它的期望和方差都是lamada。这里具体指0.75的加载因子情况下,同一个桶发生哈希冲突的可能性

是0.5.默认容量是16,加载因子是0.75,意味着如果存的元素个数超过16*0.75=12 (threshod=capacity * load factor)将要rehash,这是非常消耗资源的,因为它将原来的结点遍历重新分布,所以初始化设置个数比较关键。

其中加载因子越大,虽省空间,但是哈希冲突大,get/put性能不好;加载因子过小又浪费空间。所以0.75是API推荐时间空间折中的数据。

同时指出,初始容量一定是2的次方,即使你写的不是最后也被写成了大于该数的最小的2的次方。容量是结点数组的长度,一般来说,为了hash更加分散,该长度会使用素数,但是这里没有。

它的hash定位,是使用key的hashcode高位参与运算,然后对该长度求余,最后定位到桶的位置。

关于泊松分布,传送阮一峰的两篇博客:泊松分布和指数分布:10分钟教程 、泊松分布与美国枪击案

其中当有哈希冲突时,原先做法都是添加到该桶的链表中,但是Java 1.8做了个优化,当元素不小于MIN_TREEIFY_CAPACITY=64时,

不小于链表的阀值(默认8)会把链表转为树(TreeMap),这样查询更快了。当该桶结点减少时,达到阀值(默认6)还会转化成链表。


2.HashMap的安全性

1)Hash Collision DoS 。有人曾用HashMap的冲突的弱点发起了Hash Collision DoS 攻击制造安全事故。通过提交表单(表单就是HashMap实现),

设计好极端的哈希冲突,在极端情况下,可以让全部结点hash到同一个桶位置的链表上,插入时间复杂度O(n),查询时间复杂度O(n).具体可以看陈皓博客:Hash Collision DoS 问题

2)HashMap本身不支持并发。多个线程下容易在resize的时候发生死循环,如果要支持并发,可以考虑ConcurrentHashMap,或者

Collections.synchronizedMap,或者通过底层提供的CAS实现,实现无锁HashMap的原理与实现。











0 0
原创粉丝点击