1. 实现原理
解决哈希冲突(哈希碰撞)的办法有很多,例如开放定址法、再散列函数法、链地址法等,HashMap采用的是链地址法。因此HashMap的底层是数据+链表的结构。
从性能考虑,对链表的操作时间复杂度是O(n),因此HashMap中的链表出现的越少,性能才会越好。
两个概念:capacity容量:默认值16;loadFactor加载因子:默认值0.75。
2. 几个问题
1. 为何HashMap的数组长度一定要是2的次幂?
方便扩容后的resize操作。扩容后只有一位差异,方便快速更新扩容后的位置。
2. 为什么Map桶中个数超过8个才转为红黑树?
桶中元素初始化是链表保存的,链表查找的时间复杂度是O(n),而红黑树的查找性能为O(log(n))。当链表长度很小时,遍历速度快,但链表长度增加后对查询性能有一定影响,因此引入TreeNode。
tips:TreeNode是类似于TreeMap的结构,底层是红黑树。单个TreeNode需要占用的空间约是普通Node的两倍。
当链表长度达到8则转成红黑树,但当红黑树的节点数降低到小于等于6时则恢复为链表形态。说白了就是时间和空间的平衡思想。
当hashcode离散型良好的时候,树型bin用到的概率非常小,因为数据均匀的分布在每个bin中,几乎不会有bin种链表长度达到阈值。但在随机hashcode下,离散性可能变差,因此就可能导致不均匀的数据分布。不过理想情况下,链表长度符合泊松分布,一个链表长度达到8个元素的概率为0.00000006低于千万分之一。
事实上,链表长度超过 8 就转为红黑树的设计,更多的是为了防止用户自己实现了不好的哈希算法时导致链表过长,从而导致查询效率低,而此时转为红黑树更多的是一种保底策略,用来保证极端情况下查询的效率。
3.资料扩充
-
解决hash冲突中的开放定址法
当发生地址冲突时,按照某种方法继续探测哈希表中的其他存储单元,直到找到空位置为止。注意对于利用开放地址法处理冲突所产生的哈希表中删除一个元素时需要谨慎,不能直接地删除,因为这样将会截断其他具有相同哈希地址的元素的查找地址,所以,通常采用设定一个特殊的标志以示该元素已被删除。