1.hashmap转红黑树的时机:

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

在treeifyBin方法中还会判断:
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize()
;
当tab的长度也就是cap容量小于64时会resize而不是直接转化为红黑树.
综上:hashmap转红黑树的时机为当链表长度大于等于8并且容量大于等于64时。
 
2.hashmap put过程
源码如下:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
  //tab为node数组,p为hash运算后的下标i的node节点,n为tab长度,i为node数组下标
Node<K,V>[] tab; Node<K,V> p; int n, i;
  //hashmap采用懒加载思想,只有真正用的时候才会初始化
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
  //里面有个知识点 运算符的优先级 ()>&>= 所以先用长度-1和hash值与运算再赋给坐标i
if ((p = tab[i = (n - 1) & hash]) == null)
    //tab[i]空直接插入
tab[i] = newNode(hash, key, value, null);
else {
  //e为后面要操作的node节点 k为node的key p为已存在的下标i的node节点
Node<K,V> e; K k;
  //常问的重写hashcode和equals方法答案就在下面这句话
  //当p和e的hash值相同并且引用p,k相同或者key调用equals方法相同时直接覆盖
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
  //将把p赋给e,后续e都是我们默认操作的put的key的节点
e = p;
else if (p instanceof TreeNode)
  //如果是树则使用其他的put方法
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);
            //尝试转红黑树 
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
          //找到存在的key 跳出循环
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
 //上面链表遍历有3种结果 1.node链表长度小于8没有找到对应key,直接尾插法new一个node
              2.node链表大于8,尝试红黑树转换
              3.node链表长度小于8找到对应的key,跳出循环,此时e是有值
              所以我们说e是最后我们操作存在key的node节点
     //返回旧值,放入新值
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
  //是否扩容
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}

3.hashmap resize过程

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

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

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

Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}

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. Casdoor + OAuth 实现单点登录 SSO

    简介 Casdoor 是一个基于 OAuth 2.0 / OIDC 的中心化的单点登录(SSO)身份验证平台,简单来说,就是 Casdoor 可以帮你解决用户管理的难题,你无需开发用户登录.注册等与用 ...

  2. AcWing 165. 小猫爬山 DFS

    165. 小猫爬山 https://www.acwing.com/problem/content/description/167/ 题目 思路 dfs每一个小猫,对于要不要开新车的状态再进行dfs(注 ...

  3. 2021.07.20 P3951 小凯的疑惑(最大公因数,未证)

    2021.07.20 P3951 小凯的疑惑(最大公因数,未证) 重点: 1.最大公因数 题意: 求ax+by最大的表示不了的数(a,b给定 x,y非负). 分析: 不会.--2021.07.20 代 ...

  4. 2022最新IntellJ IDEA的zheng开发部署文档

    目录 前景提示 一.环境整合 构建工具(参考工具部署方式) 二.git 导入编译器 三.模块描述浅析 四.配置文档 1.总配置 2.数据库配置 3.密码设置 4.配置建议 五.在IDEA中执行MySQ ...

  5. python学习-Day27

    目录 今日内容详细 动态方法与静态方法 动态方法 绑定给对象的方法 绑定给类的方法 静态方法 继承 继承的含义 继承的目的 继承的基本使用 继承的本质 名字的查找顺序 不继承的情况下 单继承的情况下 ...

  6. python学习番外篇——字符串的数据类型转换及内置方法

    目录 字符串的数据类型转换及内置方法 类型转换 内置方法 优先掌握的方法 需要掌握的方法 strip, lstrip, rstrip lower, upper, islower, isupper 插入 ...

  7. 安装与基本配置DHCP服务器

    一,安装DHCP服务器角色 1,打开[开始]→[管理工具]→[服务器管理器]→"仪表板"选项的[添加角色和功能],持续单机[下一步]按钮, 直至出现下图所示的"选择服务器 ...

  8. 【代理是什么?】nginx快速入门+反向代理hexo个人博客

    @ 目录 前言 本文说明 请大家务必查看 工作原理 正向代理 反向代理 环境准备 详细版 入门:搭建步骤 配置阿里云epel源: yum安装nginx: 启动nginx: 配置default.conf ...

  9. 【论文笔记】Federated Learning for Wireless Communications: Motivation, Opportunities, and Challenges(综述)

    Federated Learning for Wireless Communications: Motivation, Opportunities, and Challenges Authors So ...

  10. 实践GoF的23种设计模式:建造者模式

    摘要:针对这种对象成员较多,创建对象逻辑较为繁琐的场景,非常适合使用建造者模式来进行优化. 本文分享自华为云社区<[Go实现]实践GoF的23种设计模式:建造者模式>,作者: 元闰子. 简 ...