结论是目前能触发转化的两个条件是:一个是链表的长度达到8个,一个是数组的长度达到64个

为什么要触发这个转换,目前官方的解释:

Because TreeNodes are about twice the size of regular nodes, we use them only when bins contain enough nodes to warrant use (see TREEIFY_THRESHOLD). And when they become too small (due to removal or resizing) they are converted back to plain bins. In usages with well-distributed user hashCodes, tree bins are rarely used. Ideally, under random hashCodes, the frequency of nodes in bins follows a Poisson distribution (http://en.wikipedia.org/wiki/Poisson_distribution) with a parameter of about 0.5 on average for the default resizing threshold of 0.75, although with a large variance because of resizing granularity. Ignoring variance, the expected occurrences of list size k are (exp(-pow(0.5, k) / factorial(k)). The first values are:

0: 0.60653066

1: 0.30326533

2: 0.07581633

3: 0.01263606

4: 0.00157952

5: 0.00015795

6: 0.00001316

7: 0.00000094

8: 0.00000006

more: less than 1 in ten million

翻译过来大概的意思是:理想情况下使用随机的哈希码,容器中节点分布在hash桶中的频率遵循泊松分布(具体可以查看http://en.wikipedia.org/wiki/Poisson_distribution),按照泊松分布的计算公式计算出了桶中元素个数和概率的对照表,可以看到链表中元素个数为8时的概率已经非常小,再多的就更少了,所以原作者在选择链表元素个数时选择了8,是根据概率统计而选择的

JDK1.8中对hashmap底层对实现进行了优化,引入了红黑树对数据结构和扩容优化等,先看下源码:

/*** Implements Map.put and related methods.** @param hash hash for key* @param key the key* @param value the value to put* @param onlyIfAbsent if true, don't change existing value* @param evict if false, the table is in creation mode.* @return previous value, or null if none*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;if ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;//如果当前的bucket里面已经是红黑树的话,执行红黑树的添加操作 else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);// TREEIFY_THRESHOLD = 8,判断如果当前bucket的位置链表长度大于8的话就将此链表变成红黑树if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;
}
这个方法执行对操作是,先通过hash计算要添加的key准备插入的槽位,如果key是一样的,则根据设置的参数是否执行覆盖,如果相应的槽位是空的话直接插入,如果对应的槽位有值则判断是红黑树结构还是链表结构,
链表的话则顺着链表寻找,如果找到一样的key,则根据参数选择覆盖,没有找到则链接到链表最后面,链表项的数目大于8则对其进行树化,如果是红黑树结构则按照树的添加方式进行添加操作
进行数化对添加操作是通过treeifyBin方法,我们来看看这个方法:
/*** Replaces all linked nodes in bin at index for given hash unless* table is too small, in which case resizes instead.*/
final void treeifyBin(Node<K,V>[] tab, int hash) {int n, index; Node<K,V> e;//MIN_TREEIFY_CAPACITY = 64,这里是重点,如果table小于64,那么是走的扩容resize的方法,超过这个数字,才会走到else的TreeNode的构建if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)//自动扩容,后续专门一篇文章介绍这个resize();// 通过hash求出bucket的位置。else if ((e = tab[index = (n - 1) & hash]) != null) {TreeNode<K,V> hd = null, tl = null;do {// 将每个节点包装成TreeNodeTreeNode<K,V> p = replacementTreeNode(e, null);if (tl == null)hd = p;else {// 将所有TreeNode连接在一起此时只是链表结构p.prev = tl;tl.next = p;}tl = p;} while ((e = e.next) != null);if ((tab[index] = hd) != null)hd.treeify(tab);}
}
这个方法的操作是将目前的9个项通过链表的方式链接在一起,以他为基础,构建红黑树
看下TreeNode类的源码,发现对红黑树的操作都是在TreeNode内部
static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
static <K,V> TreeNode<K,V> rotateRight(TreeNode<K,V> root,
static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,/*** Forms tree of the nodes linked from this node.*/
final void treeify(Node<K,V>[] tab) {TreeNode<K,V> root = null;
//遍历传入的链表for (TreeNode<K,V> x = this, next; x != null; x = next) {next = (TreeNode<K,V>)x.next;x.left = x.right = null;
//为根结点赋值if (root == null) {x.parent = null;x.red = false;root = x;}else {
//x是当前访问链表中的项K k = x.key;int h = x.hash;Class<?> kc = null;
//此时红黑树已经有了根节点,上面获取了当前加入红黑树的项的key和hash值进入核心循环,从root开始是一个自顶向下的方式遍历添加
//for循环没有控制条件,由代码内部break跳出循环for (TreeNode<K,V> p = root;;) {
//dir:directory,ph:parent hashint dir, ph;K pk = p.key;
//比较当前项与当前树中访问节点hash值判断加入项的路径,-1为左子树,+1为右子树if ((ph = p.hash) > h)dir = -1;else if (ph < h)dir = 1;else if ((kc == null &&(kc = comparableClassFor(k)) == null) ||(dir = compareComparables(kc, k, pk)) == 0)dir = tieBreakOrder(k, pk);
//xp:x parent,找到符合x添加条件的节点TreeNode<K,V> xp = p;if ((p = (dir <= 0) ? p.left : p.right) == null) {x.parent = xp;
//如果xp的hash值大于x的hash值,将x添加在xp的左边,否则,添加在xp的右边if (dir <= 0)xp.left = x;elsexp.right = x;
//添加节点后,维护添加后红黑树的红黑结构root = balanceInsertion(root, x);
//跳出循环,代表当前链表中的项成功的添加到了红黑树中break;}}}}moveRootToFront(tab, root);
}

HashMap什么时候由链表转红黑树相关推荐

  1. HashMap为什么用链表加红黑树?目的是什么?原理是什么

    关于HashMap的详解文章: 链接: HashMap源码研究--源码一行一行的注释 文章目录 1为什么用链表? 2为什么用红黑树? 2.1 红黑树概述 2.2 红黑树性质 为什么满足上面的性质,红黑 ...

  2. 为什么HashMap中链表转红黑树的阀值是8?

    在JDK1.8以后,HashMap中引入红黑树,主要原因为: 当一个桶(Bucket)中的元素过度填充时,链表的查找效率将会大大下降,因此在适当的时候,转换链表为红黑树,可以在桶过度填充时提高查询效率 ...

  3. Java笔记整理五(Iterator接口,泛型,常见数据结构(栈,队列,数组,链表,红黑树,集合),jdk新特性,异常,多线程,Lambda表达式)

    Java笔记整理五 1.1Iterator接口 Collection接口与Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象 ...

  4. 简单的数据结构介绍(栈、队列、数组、链表、红黑树)

    学习目标: 了解常见的数据结构 学习内容: 栈.队列.数组.链表.红黑树 学习产出: 1. 数据结构 数据结构 : 就是数据用什么样的方式组合在一起 常见的数据结构有: 栈, 队列, 数组, 链表和红 ...

  5. Hashmap解决hash冲突为什么使用红黑树

    首先,在解决这个问题之前我们要先了解hash冲突是什么. hash冲突 hashmap在添加新的键值对的时候,会根据key值通过一个函数计算出一个结果作为地址值,根据这个地址值将其键值对插入到对应的位 ...

  6. 常见的数据结构——栈、队列、数组、链表和红黑树

    链表 链表:linked list,由一系列结点node(链表中每一个元素称为结点)组成,结点可以在运行时i动态生成. 每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指 ...

  7. 数据结构:栈、队列、数组、链表、红黑树结构的特点

    * 1.栈结构:特点:先进后出,类似子弹夹 * 2.队列的结构: 特点:先进先出 * 3.数组结构: 特点:查询快,增删慢 * 为什么数组查询快? 因为数组的地址是连续的,我们可以通过数组的首地址查到 ...

  8. JAVA day16、17 数据结构(栈、队列、数组、链表、红黑树)

    一.什么叫数据结构? 数据结构是相互之间存在一种或多种特定关系的数据元素的集合,即带"结构"的数据元素的集合."结构"就是指数据元素之间存在的关系,分为逻辑结构 ...

  9. Java基础加强重温_05:Iterator迭代器、增强for循环、集合综合案例-斗地主、数据结构(栈、队列、数组、链表、红黑树)、List接口、Set接口

    摘要: Java基础加强重温_05: Iterator迭代器(指针跟踪元素). 增强for循环(格式.底层). 集合综合案例-斗地主(代码规范抽取代码,集合元素打乱). 数据结构[栈(先进后出,子弹夹 ...

  10. 1.Hashmap 解决hash冲突为什么使用红黑树

    1.  hash冲突指的是在向Hash表中存数据时,首先要用Hash函数计算出该数据要存放的地址.但是在这个地址中已经有值存在,所以这个时候就发生了Hash冲突.也就是说:值不同的元素可能会映象到哈希 ...

最新文章

  1. NR 5G MAC媒体接入控制
  2. Enterprise Library 4.1 Application Settings 快速使用图文笔记
  3. Unix——百度百科
  4. 爬虫 spider06——解析数据
  5. RUNOOB python练习题5
  6. python3.x+requests 爬取网站遇到中文乱码的解决方案
  7. Unity动态对象优化
  8. 对外提供dubbo服务的最佳实践
  9. mysql5.5默认引擎,在MySQL5.5以上系统中,默认的存储引擎是( )。
  10. 基于RRT算法的路径规划实现(matlab)
  11. 关于React Hooks使用
  12. Protel99seMEX3
  13. 基于Material Studio软件使用第一性原理预测AlAs的晶格参数
  14. 华为云数据容灾服务,如何守护企业数据安全
  15. 夜神模拟器-软件apk存放目录
  16. android获取sdcard文件,android读取SDCard任意路径下的文件
  17. C# Math函数汇总
  18. [零基础学JAVA]Java SE实战开发-37.MIS信息管理系统实战开发[文件保存](1)
  19. 微信聊天记录如何才能永久删除?这些彻底删除的方法你知道多少
  20. Win32之ShowWindow

热门文章

  1. xp任务栏一直闪跳怎么办_电脑的任务栏在不停闪烁,这个是什么问题引起的
  2. Windows Server 2012 R2 打印服务器的设置与管理-深博-专题视频课程
  3. secureCRT 7.3.6 winxp版本
  4. 【rmzt:进击的巨人炫彩主题】
  5. 计算机组装与维护思考问题,计算机组装与维护心得体会
  6. python发邮件smtplib+mail
  7. 计算机怎样更新卡驱动,显卡驱动怎么升级
  8. 免费获取卫星影像的网站你知道几个?
  9. 如何批量设置 Word 文档的打开密码?
  10. SSL安全证书不受信任怎么办