HashMap源码验证,在JDK8中新增红黑树详解
红黑树查询演示,红黑树它解决了链表查询过慢的问题
我们插入的链表过长之后,因为我们的链表都是从头部开始进行查询,所以它会比较慢。
我们现在引入到我们的红黑树之后呢,它就不会有这个问题,那它是怎么去解决呢?
我们来看一看,刚才我们还是去插入这7个元素
我们一个红黑树的,它的一个结构呢,就七个节点的元素是这样的,
来我们看一下,我们用红黑树去查这个007要查多少次呢?
https://img1.tuicool.com/BzeIB3m.gif
一次,两次,三次,四次,
也就是说我们用红黑树啊,只要查四次,而刚才我这个用链表是要查七次,
那我是不是减少了三次的这个查询次数,那同时我们就非常的明显,
如果我们这边是70万,这边只要查40万,那我就减少了30万的查询,
那这样的话是不是性能将近提高了一倍,能理解吧,
所以我们这个红黑树,它就是去解决我们链表查询过慢的一个问题。
在JDK8里面,HashMap并不是一上来就直接用这个红黑树
但是呢,我们这个HashMap,在JDK8里面,它并不是一上来就直接用这个红黑树,
一上来它默认还是先用链表, 只有当我们的这个链表达到一个阈值的情况下,
它才会用这个红黑树,那你们知道这个阈值是多少吗?
阈值是8,那同学们我想问一下,为什么是8呢?
看HashMap源码
咱们看这个HashMap的一个源码,Shift+Shift,搜索HashMap,
同时勾选 Include non-project items,如下图
在这个地方,HashMap呢,我们主要看它的这个put方法,它调用了 putVal方法,
那这个 putVal 方法里面呢,它有一个值,这个条件它就等于8,
那为什么我们的链表的长度大于8之后呢,就会用红黑树,
如果不大于8的话,它是用链表,大家你们知道为什么吗?
来,其实在这里,它也不是等于8,严格意义上它是用8减1,
<pre class="hljs awk" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
大家看它的条件,你看如果这个条件满足情况,它就调这个方法 treeifyBin,
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin (tab, hash);
break;
treeifyBin 这个方法,
你看一上来的话,它这里是用什么?这里是用的是咱们这个链表,
你看里面有 hash(哈希),有 key 和 value,next 对吧?开始用链表,
但是当我们这个条件执行 else if 的时候,他用这个 TreeNode,
而这个 TreeNode 就是咱们这个红黑树,你看里面,还有 boolean 类型是否为红节点,
我们的上一个节点、右节点、左节点,还有我们的根节点,
为什么红黑树查询快,插入慢?
为什么红黑树,它的查询性能就一定会比咱们的这个链表快呢?
第一个就取决于它这个查询的规律不一样对吧!
那么我想问一下,那为什么就一定要有一个阈值?为什么还要先要用链表的?
那为什么一上来不直接用红黑树呢?原因是什么?
而且我们明明已经知道链表,它已经会带来这个查询的这个慢的原因
我们要知其然知其所以然,那么这个东西呢,其实就是鱼和熊掌不可兼得,
那我来跟大家去讲一下,刚刚在这个地方,我是讲的是红黑素这个查询,但是同学们,
我来给大家看一下它的插入,它的插入你来认真去看这个节点,你就明白了!
来我把这个东西给你再演示一遍
https://img1.tuicool.com/BzeIB3m.gif
你们注意看啊,插入第一个,插入第二个
好,当我去插第三个值的时候,你们注意看啊,看到没有,
我们的这个001,它进行了一次左移(左旋)。
也就是说我们的红黑树,它的查询是比较快,但是它的插入比较慢,为什么呢?
因为它需要对我们的值进行左移,进行左移(左旋)的话,他们的数据是进行需要进行交换的,
所以在这个过程中,我可以很负责任的告诉大家,我们的这个插入,
我们的链表绝对会比我们的红黑树要更快一些。
所以我们来看一看链表它的特性是什么?它的特性是插入删除会比较快,
因为它只要O(1)的操作,而咱们这个红黑树很明显不是O(1)的操作,
所以这就是为什么需要有一个阈值,这个阈值就是使得它们两者达到一个平衡。
这就是鱼和熊掌不可兼得!
那在这里我就跟大家讲,为什么我们的红黑树,它查询为什么会快呢?原因你们知道为什么吗?
因为我们的红黑树,它需要维护一个结构,维护一个什么样的结构呢?
我把这个技巧告诉大家,这个技巧就是:小中大,左中右
那这个是一个什么样的一个结构呢?
那大家我们来看啊,当前我这个红黑树数的一个节点,根节点是002,
我们要去查询的这个值是007,而比002小的是不是001。
你看比它002小的,是不是在左边,比002大的,你看003、004、005、006、007都在右边,
大家能理解这个点吗?这就是小中大,左中右这个结构,
你任何一个节点的数据都是这样的。
来我们来看004啊,你看004也是一样,比004小的,都在左边,
你看001、002、003是不是都在左边。
比004大的,是不是在右边,你看005、006、007。
同样,006也是一样,这就是小中大,左中右这个结构,
如果你能理解这个点,那你就明白了,所以为什么我们的红黑素在插入的时候会慢,
因为它要维护这样的一个结构,它要维护一个小中大,左中右的一个结构,
所以我们为什么查007的话,为什么快?
因为我们首先呢,007跟002进行比较对吧,所以发现这个007大
就这样,007依次会002、004、006、007 进行比较,直到找到相等的007
#java##程序员#