1.hashmap转红黑树的时机:

  1. for (int binCount = 0; ; ++binCount) {
  2. if ((e = p.next) == null) {
  3. p.next = newNode(hash, key, value, null);
  4. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
  5. treeifyBin(tab, hash);
  6. break;
  7. }
上面代码在putval方法中:判断了当不存在的key插入map时,如果往node节点一直遍历当大于等于8次时,会转化尝试转化为红黑树,注意是尝试。

在treeifyBin方法中还会判断:
  1. if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
  2. resize()
  3. ;
当tab的长度也就是cap容量小于64时会resize而不是直接转化为红黑树.
综上:hashmap转红黑树的时机为当链表长度大于等于8并且容量大于等于64时。
 
2.hashmap put过程
源码如下:
  1. final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
  2. boolean evict) {
  3.   //tab为node数组,p为hash运算后的下标i的node节点,n为tab长度,i为node数组下标
  4. Node<K,V>[] tab; Node<K,V> p; int n, i;
  5.   //hashmap采用懒加载思想,只有真正用的时候才会初始化
  6. if ((tab = table) == null || (n = tab.length) == 0)
  7. n = (tab = resize()).length;
  8.   //里面有个知识点 运算符的优先级 ()>&>= 所以先用长度-1和hash值与运算再赋给坐标i
  9. if ((p = tab[i = (n - 1) & hash]) == null)
  10.     //tab[i]空直接插入
  11. tab[i] = newNode(hash, key, value, null);
  12. else {
  13.   //e为后面要操作的node节点 k为node的key p为已存在的下标i的node节点
  14. Node<K,V> e; K k;
  15.   //常问的重写hashcode和equals方法答案就在下面这句话
  16.   //当p和e的hash值相同并且引用p,k相同或者key调用equals方法相同时直接覆盖
  17. if (p.hash == hash &&
  18. ((k = p.key) == key || (key != null && key.equals(k))))
  19.   //将把p赋给e,后续e都是我们默认操作的put的key的节点
  20. e = p;
  21. else if (p instanceof TreeNode)
  22.   //如果是树则使用其他的put方法
  23. e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
  24. else {
  25.   //下面是链表的遍历过程
  26. for (int binCount = 0; ; ++binCount) {
  27. if ((e = p.next) == null) {
  28. p.next = newNode(hash, key, value, null);
  29.             //尝试转红黑树 
  30. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
  31. treeifyBin(tab, hash);
  32. break;
  33. }
  34.           //找到存在的key 跳出循环
  35. if (e.hash == hash &&
  36. ((k = e.key) == key || (key != null && key.equals(k))))
  37. break;
  38. p = e;
  39. }
  40. }
  41.  //上面链表遍历有3种结果 1.node链表长度小于8没有找到对应key,直接尾插法new一个node
  42.               2.node链表大于8,尝试红黑树转换
  43.               3.node链表长度小于8找到对应的key,跳出循环,此时e是有值
  44.               所以我们说e是最后我们操作存在keynode节点
  45.      //返回旧值,放入新值
  46. if (e != null) { // existing mapping for key
  47. V oldValue = e.value;
  48. if (!onlyIfAbsent || oldValue == null)
  49. e.value = value;
  50. afterNodeAccess(e);
  51. return oldValue;
  52. }
  53. }
  54. ++modCount;
  55.   //是否扩容
  56. if (++size > threshold)
  57. resize();
  58. afterNodeInsertion(evict);
  59. return null;
  60. }

3.hashmap resize过程

resize总结:1.获取新的容量和临界值

      2.创建新表将hashmap tab rehash 元素以原来下标或者二次幂的偏移量下标移动

  1. /**
  2. * Initializes or doubles table size. If null, allocates in
  3. * accord with initial capacity target held in field threshold.
  4. * Otherwise, because we are using power-of-two expansion, the
  5. * elements from each bin must either stay at same index, or move
  6. * with a power of two offset in the new table.
  7. *
  8. * @return the table
  9. */
       //二次幂展开,扩容后元素要么是原有下标,要么以二次幂偏移量
       //exp:假设容量为16 key的hash为16 1111&10000=0存放在tab[0]中 如果扩容后容量为32 那么rehash时11111&10000 就是tab[16] 所以以二次幂的偏移量移动
  10. final Node<K,V>[] resize() {
  11. Node<K,V>[] oldTab = table;
         //oldTab==null 为了兼容初始化
  12. int oldCap = (oldTab == null) ? 0 : oldTab.length;
  13. int oldThr = threshold;
  14. int newCap, newThr = 0;
  15. if (oldCap > 0) {
           //当容量大于允许最大容量时临界点为Integer最大值
  16. if (oldCap >= MAXIMUM_CAPACITY) {
  17. threshold = Integer.MAX_VALUE;
  18. return oldTab;
  19. }
           //正常扩容 临界点和容量都扩大两倍
  20. else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
  21. oldCap >= DEFAULT_INITIAL_CAPACITY)
  22. newThr = oldThr << 1; // double threshold
  23. }//使用临界值覆盖容量
  24. else if (oldThr > 0) // initial capacity was placed in threshold
  25. newCap = oldThr;
  26. else { // zero initial threshold signifies using defaults
  27. //当oldThr是0时初始化
           newCap = DEFAULT_INITIAL_CAPACITY;
  28. newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
  29. }
         //临界值还是为0
  30. if (newThr == 0) {
  31. float ft = (float)newCap * loadFactor;
  32. newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
  33. (int)ft : Integer.MAX_VALUE);
  34. }
  35. threshold = newThr;
  36. @SuppressWarnings({"rawtypes","unchecked"})
         //创建新tab
  37. Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
  38. table = newTab;
  39. if (oldTab != null) {
            //遍历oldTab数组
  40. for (int j = 0; j < oldCap; ++j) {
  41. Node<K,V> e;
  42. if ((e = oldTab[j]) != null) {
  43. oldTab[j] = null;
  44. if (e.next == null)
                  //该下标只有一个node时直接放入新tab中
  45. newTab[e.hash & (newCap - 1)] = e;
  46. else if (e instanceof TreeNode)
  47. ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
  48. else { // preserve order
                   //下面的代码分为了两个链表 其实就是把hash大于等于cap的和小于的分开 小于的在原位 大于等于的按照oldcap偏移量下标存放
                   //并且采用了尾插法

  49. Node<K,V> loHead = null, loTail = null;
  50. Node<K,V> hiHead = null, hiTail = null;
  51. Node<K,V> next;
  52. do {
  53. next = e.next;
  54. if ((e.hash & oldCap) == 0) {
  55. if (loTail == null)
  56. loHead = e;
  57. else
  58. loTail.next = e;
  59. loTail = e;
  60. }
  61. else {
  62. if (hiTail == null)
  63. hiHead = e;
  64. else
  65. hiTail.next = e;
  66. hiTail = e;
  67. }
  68. } while ((e = next) != null);
  69. if (loTail != null) {
  70. loTail.next = null;
  71. newTab[j] = loHead;
  72. }
  73. if (hiTail != null) {
  74. hiTail.next = null;
  75. newTab[j + oldCap] = hiHead;
  76. }
  77. }
  78. }
  79. }
  80. }
  81. return newTab;
  82. }

HashMap1.8常见面试问题的更多相关文章

  1. Android开发面试经——6.常见面试官提问Android题②(更新中...)

    版权声明:本文为寻梦-finddreams原创文章,请关注:http://blog.csdn.net/finddreams 关注finddreams博客:http://blog.csdn.net/fi ...

  2. Android开发面试经——5.常见面试官提问Android题①

    版权声明:本文为寻梦-finddreams原创文章,请关注:http://blog.csdn.net/finddreams 关注finddreams博客: http://blog.csdn.net/f ...

  3. 机器学习&数据挖掘笔记(常见面试之机器学习算法思想简单梳理)

    机器学习&数据挖掘笔记_16(常见面试之机器学习算法思想简单梳理) 作者:tornadomeet 出处:http://www.cnblogs.com/tornadomeet 前言: 找工作时( ...

  4. [转]机器学习&数据挖掘笔记_16(常见面试之机器学习算法思想简单梳理)

    机器学习&数据挖掘笔记_16(常见面试之机器学习算法思想简单梳理) 转自http://www.cnblogs.com/tornadomeet/p/3395593.html 前言: 找工作时(I ...

  5. java异常常见面试问题

    java异常常见面试问题 一.java异常的理解 异常主要是处理编译期不能捕获的错误.出现问题时能继续顺利执行下去,而不导致程序终止,确保程序的健壮性. 处理过程:产生异常状态时,如果当前的conte ...

  6. (转)C/C++ 程序设计员应聘常见 面试笔试 试题深入剖析

    C/C++ 程序设计员应聘常见 面试笔试 试题深入剖析 http://www.nowcoder.com/discuss/1826?type=2&order=0&pos=23&p ...

  7. Hibernate的10个常见面试问题及答案

    在Java J2EE方面进行面试时,常被问起的Hibernate面试问题,大多都是针对基于Web的企业级应用开发者的角色的.Hibernate框架在Java界的成功和高度的可接受性使得它成为了Java ...

  8. Android常见面试笔试题目

    Android常见面试笔试题目 1.在多线程编程这块,我们经常要使用Handler,Thread和Runnable这三个类,那么他们之间的关系你是否弄清楚了呢? 答:可以处理消息循环的线程,他是一个拥 ...

  9. BAT网络运维常见面试题目总结

    BAT常见面试题目总结 Author:Danbo 2015-7-11 TCP/IP详解鸟哥Linux的书网络安全ping的原理make的过程文件有哪些类型各种Linux发行版的区别.有关suid的作用 ...

随机推荐

  1. flex布局 一行4个元素 后面不够4个元素对齐

    html 父元素 .container { display: flex; flex-wrap: wrap;} 子元素.list { width: 24%; height: 100px; backgro ...

  2. 关于Mysql索引的数据结构

    索引的数据结构 1.为什么使用索引 概念: 索引是存储索引用于快速找到数据记录的一种数据结构,就好比一本书的目录部分,通过目录中对应的文章的页码,便可以快速定位到需要的文章,Mysql 中也是一样的道 ...

  3. GO语言学习——切片三 append()方法为切片添加元素、切片的扩容策略、使用copy()函数复制切片、从切片中删除元素

    append()方法为切片添加元素 注意:通过var声明的零值切片可以在append()函数直接使用,无需初始化. var s []int s = append(s, 1, 2, 3) append( ...

  4. C# 有关List<T>的Contains与Equals方法

    [以下内容仅为本人在学习中的所感所想,本人水平有限目前尚处学习阶段,如有错误及不妥之处还请各位大佬指正,请谅解,谢谢!]   !!!观前提醒!!! [本文内容可能较为复杂,虽然我已经以较为清晰的方式展 ...

  5. kill -9 进程杀不掉,怎么办?

    关注「开源Linux」,选择"设为星标" 回复「学习」,有我为您特别筛选的学习资料~ 用ps和grep命令寻找僵尸进程 ps -A -ostat,ppid,pid,cmd | gr ...

  6. 为什么 IPv6 难以取代 IPv4

    网络层协议承担了分组(Packet)转发和路由选择两大功能,它能够为上层提供在不同主机之间运输分组的职责,IP 协议作为网络层协议,它虽然只能提供无连接的.不可靠的服务,但是它在今天的互联网中起到了极 ...

  7. Flutter异步与线程详解

    一:前言 - 关于多线程与异步 关于 Dart,我相信大家都知道Dart是一门单线程语言,这里说的单线程并不是说Dart没有或着不能使用多线程,而是Dart的所有API默认情况下都是单线程的.但大家也 ...

  8. Oracle19c单实例数据库配置OGG单用户数据同步测试

    目录 19c单实例配置GoldenGate 并进行用户数据同步测试 一.数据库操作 1.开启数据库附加日志 2.开启数据库归档模式 3.开启goldengate同步 4.创建goldengate管理用 ...

  9. 基于DEM的坡度坡向分析

    坡度坡向分析方法 坡度(slope)是地面特定区域高度变化比率的量度.坡度的表示方法有百分比法.度数法.密位法和分数法四种,其中以百分比法和度数法较为常用.本文计算的为坡度百分比数据.如当角度为45度 ...

  10. 04 Springboot 格式化LocalDateTime

    Springboot 格式化LocalDateTime 我们知道在springboot中有默认的json解析器,Spring Boot 中默认使用的 Json 解析技术框架是 jackson.我们点开 ...