• 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

  • 相关阅读:
    java8-使用流-2
    JavaScript中用jquery获取鼠标的定位和对节点文本操作
    爬虫项目(12):正则、多线程抓取腾讯动漫,Flask展示数据
    redis的windows系统的安装教程
    《华为战略管理法:DSTE实战体系》作者谢宁老师受邀为某电力上市集团提供两天的《成功的产品管理及产品经理》内训。
    Graph (discrete mathematics)
    c++桥接模式,中介者模式应用实现状态跳转
    Qt 生成应用程序(二)软件多图标与文件操作
    PostgreSQL 文章下架 与 热更新和填充可以提升数据库性能
    【C++进阶之路】特殊类的设计
  • 原文地址:https://blog.csdn.net/Java_ttcd/article/details/126240697