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. 【LeetCode】567. 字符串的排列

    567. 字符串的排列 知识点:字符串:滑动窗口 题目描述 给你两个字符串 s1 和 s2 ,写一个函数来判断 s2 是否包含 s1 的排列.如果是,返回 true :否则,返回 false . 换句 ...

  2. vite初使用随记

    vite的安装 按照官网文档来看,并不难. 先检查自己电脑node的版本与npm的版本/yarn的版本 可以直接用yarn安装,yarn create vite 这是最原始的安装,即类似于安装vue- ...

  3. 最新MATLAB R2020b超详细安装教程(附完整安装文件)

    摘要:本文详细介绍Matlab R2020b的安装步骤,为方便安装这里提供了完整安装文件的百度网盘下载链接供大家使用.从文件下载到证书安装本文都给出了每个步骤的截图,按照图示进行即可轻松完成安装使用. ...

  4. SQL注入绕过总结

    花括号绕过 select{x password}from{database.user} union select 1,{x 2},3 特征字符大小写绕过 UniOn SEleCt 1,2,3 MYSQ ...

  5. 从0开始基于Webpack5 搭建HTML+Less 前端工程

              基于Webpack5 搭建HTMl+Less的前端项目 新建一个文件夹(比如命名为webpack) 用编辑器打开该文件夹,并在编辑器的终端执行 npm init -y 自动创建pa ...

  6. 【MyBatis】多关键字的模糊查询

    目录 情景分析 题目 要求 相关代码 Mapper.java Impl.java mapper.xml test.java 思路分析 1.最开始,参数没有使用List,引起的问题 2.Mybatis的 ...

  7. 实体linux服务器-由自动ip改为固定ip后,无法上网问题--配置问题解法

    新入公司,研发产业为零,开始搞. linux之前是自动获取ip地址的,网上搜索的帖子,耍流氓的居多,不能上网的原因很多,我这个是配置不对,看是否与你的一样. 1.首先看下当前电脑网卡,根据地址可以判断 ...

  8. Docker中级篇,看这篇就对了

    点击上方"开源Linux",选择"设为星标"回复"学习"获取独家整理的学习资料! 姊妹篇: Docker容器网络-基础篇 十分钟看懂Dock ...

  9. call()、apply()、arguments

    一.call(),apply() 1.作为函数对象(指函数方法名,不带括号)的方法,需要通过函数对象调用:当对函数调用这两个方法时都会调用函数执行. <script> // 这个函数中,f ...

  10. Java 效率工具, 大幅度提高开发效率

    你是否有遇到过这样的情况,在开发过程中需要比较两列数据,但使用文本比对工具的话他是按行基准比对的,我还得对每列数据先进行排序,但排序又去哪里排, 想到 excel 可以排序 , 折腾下来,特别麻烦, ...