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。