关于hashmap:HashMap

7次阅读

共计 1422 个字符,预计需要花费 4 分钟才能阅读完成。

JDK 8 HashMap 为啥要引入红黑树?
当 HashMap 的 key 抵触过多时,比方咱们应用了不好的 hash 算法,导致 key 抵触率极高, 咱们都晓得链表的查找性能很差,所以引入红黑树是为了优化查问性能。

JDK 8 HashMap 为啥不间接用红黑树?
因为树节点所占用的空间是一般节点的两倍,所以只有当节点足够多的时候,才会应用树节点。也就是说,最开始应用链表的时候,链表是比拟短的,空间占用也是比拟少的, 查问性能都差不多, 然而当链表越来越长,链表查问越来越慢,为了保障查问效率,这时候才会舍弃链表而应用红黑树,以空间换工夫。

所以没有必要一开始就用红黑树,另外,链表较长的状况十分少见,一开始就应用红黑树反而会导致所有的状况都会占用比链表大 2 倍的空间,事与愿违,这也是一种均衡的策略。
为什么链表长度达到 8?
因为达到 8 个元素的时候,概率曾经很低了。此时树化,性价比会很高。
既不会因为链表太长 (8) 导致复杂度加大,也不会因为概率太高导致太多节点树化。
为什么红黑树转链表的阈值为 6?
次要是因为,如果也将该阈值设置于 8,那么当 hash 碰撞在 8 时,会反生链表和红黑树的不停互相激荡转换,白白浪费资源。两头有个差值 7 能够避免链表和树之间的频繁转换,
假如一下:

如果设计成链表个数超过 8 则链表转换成树结构,链表个数小于 8 则树结构转换成链表,如果 HashMap 不停的插入,删除元素,链表个数在 8 左右彷徨,就会频繁的产生红黑树转链表,链表转红黑树,效率会很低下。
putVal 办法次要做了这么几件事件:

当桶数组 table 为空时,resize()初始化 table
查找要插入的键值对是否曾经存在,onlyIfAbsent 如果为 false 且 oldValue 等于 null 则笼罩旧值
如果不存在,则将键值对链入链表中, 如果链表长度达到了 8,且数组长度小于 64,那么就从新散列,如果大于 64,则创立红黑树
判断键值对数量是否大于阈值,大于的话则进行扩容操作

resize 办法次要做了这么几件事件:
当 HashMap 中的元素个数超过数组大小 (数组长度)*loadFactor(负载因子) 时,就会进行数组扩容。loadFactor 的默认值 (DEFAULT_LOAD_FACTOR) 是 0.75。也就是说,默认状况下,数组大小为 16,那么当 HashMap 中的元素个数超过 16×0.75=12(这个值就是阈值或者边界值 threshold 值)的时候,就把数组的大小扩大为 2×16=32,即扩充一倍,而后从新计算每个元素在数组中的地位,而这是一个十分耗性能的操作,所以如果咱们曾经预知 HashMap 中元素的个数,那么预知元素的个数可能无效的进步 HashMap 的性能

计算新桶数组的容量 newCap 和新阈值 newThr
依据计算出的 newCap 创立新的桶数组,桶数组 table 也是在这里进行初始化的
将键值对节点从新映射到新的桶数组里。如果节点是 TreeNode 类型,则须要拆分红黑树。如果是一般节点,则节点按原程序进行分组
将键值对节点从新映射到新的 tab 解析:

HashMap 在进行扩容时,应用的 rehash 形式十分奇妙,因为每次扩容都是翻倍,与原来的数组长度 n 计算的 (n-1)&hash 的后果相比,只是多了一个 bit 位,所以节点要么就在原来的地位,要么就被调配到 ” 原地位 + 旧容量 ” 这个地位。那么多的这一位怎么判断是 0 还是 1 呢?:e.hash & oldCap 原容量,而后判断等不等于 0 即可。等 0 即新位是 0,不等 0 即新位是 1。

正文完
 0