【JVM】-NO.113.JVM.1 -【JDK11 HashMap详解-0-全局-put】
Style:Mac
Series:Java
Since:2018-09-10
End:2018-09-10
Total Hours:1
Degree Of Diffculty:5
Degree Of Mastery:5
Practical Level:5
Desired Goal:5
Archieve Goal:3
Gerneral Evaluation:3
Writer:kingdelee
Related Links:
http://www.cnblogs.com/kingdelee/
http://www.runoob.com/java/java-operators.html
1.解析常见的HaspMap操作背后的故事
package jdk11.map; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; public class Client { private static final Logger logger = LogManager.getLogger(Client.class); public static void main(String[] args) { Map<Father, Integer> map = new HashMap<>();
Father f1 = new Father("f1");
f1.setHashcode(1);
Father f2 = new Father("f2");
f2.setHashcode(1);
Father f3 = new Father("f3");
f2.setHashcode(1);
Father f4 = new Father("f4");
f2.setHashcode(1);
Father f5 = new Father("f5");
f2.setHashcode(1);
Father f6 = new Father("f6");
f2.setHashcode(1);
Father f7 = new Father("f7");
f2.setHashcode(1);
Father f8 = new Father("f8");
f2.setHashcode(1);
Father f9 = new Father("f9");
f2.setHashcode(1); map.put(f1, 1);
map.put(f2, 2);
map.put(f3, 3);
map.put(f4, 4);
map.put(f5, 5);
map.put(f6, 6);
map.put(f7, 7);
map.put(f8, 8);
map.put(f9, 9);
map.put(new Father("10", 1), 10);
map.put(new Father("11", 1), 11);
map.put(new Father("12", 1), 12);
map.put(new Father("13", 1), 13);
map.put(new Father("14", 1), 14);
map.put(new Father("15", 1), 15);
map.put(new Father("16", 1), 16);
map.put(new Father("17", 1), 17);
map.put(new Father("18", 1), 18);
map.put(new Father("19", 1), 19);
map.put(new Father("20", 1), 20);
map.put(new Father("21", 1), 21);
logger.info(map.get(f1));
logger.info(map.get(f2));
logger.info(map.get(new Father("21", 1))); }
} class Father {
public String name; public int hashcode; public Father(String name, int hashcode) {
this.name = name;
this.hashcode = hashcode;
} public Father(String name) {
this.name = name;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getHashcode() {
return hashcode;
} public void setHashcode(int hashcode) {
this.hashcode = hashcode;
} @Override
public int hashCode() {
return getHashcode();
} @Override
public boolean equals(Object obj) {
return name.equals(((Father) obj).name) && hashcode == (((Father) obj).hashcode);
}
}
2.new
很简单,仅仅对一些常量做初始化其中比较重要的有
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
loadFactor=0.75,扩展因子
DEFAULT_INITIAL_CAPACITY=16,初始化的容量为16
....
3.put
3.1 算出hash()
public V put(K key, V value) {
logger.info("put---------------------modCount:" + modCount);
return putVal(hash(key), key, value, false, true);
}
3.2 putVal()
声明Node[] tab,以便该数组类型做横向数组填充
进行多个判断:
当tab为空时,会创建tab,并给tab重新构建空间resize()
当需要填坑的坑位是空时,横向创建坑位
如果需要填的坑已被占,则考虑纵向创建该坑位关联节点;
考虑情况有3,1.如果是指向同一个内存地址则忽略操作;2.如果需要创建的链式节点已经进化成红黑树,则执行插入红黑树的操作;3.还不是红黑树,则执行该节点的链表创建关联链式节点操作
以上是宏观上看待put。
3.2.1 微观上看putVal()
3.2.1.1 即tab未创建时,通过resize()创建tab
if ((tab = table) == null || (n = tab.length) == 0) {
logger.info("table为null");
n = (tab = resize()).length; // 1.当未指定初始容量时,进行resize, 得到容量值赋给n=16; 获得新的节点给tab;已经存在节点时不再进来
logger.info("tab renTab");
}
如果要put的坑位是空的,就将创建该坑位Node
if ((p = tab[i = (n - 1) & hash]) == null) // i: (16-1) & 10 = 10,未存在节点的情况下,让新节点P指向数组节点tab中的hash后的节点,创建节点数组;已经存在节点时不再进来
{
logger.info("创建一个新节点,tab["+i+"]指向这个节点" + "hash:" + hash + ",value:" + value);
tab[i] = newNode(hash, key, value, null); // 仅在p节点为空的情况下,创建刚刚新节点指向hash后为空的节点的位置
}
3.2.1.2 否则执行一段非常复杂的逻辑
3.2.1.2.1
判断是否为指向统一内存地址,如果是,则break
Node<K,V> e; K k;
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))){
e = p; // hash相等 && key相等 的情况下,用节点e存储原来的已经存在的节点k
logger.info("相同对象");
}
不是同一内存地址,继续判断,需要该坑位是否已经进化成红黑树,如果是,就执行红黑树的put,不是则继续判断
else if (p instanceof TreeNode) // 是否已经进化成红黑树
{
logger.info("进化红黑树");
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
}
以上条件都不是,说明这货要以链表或者首次进化成树的方式put进来
通过for依次遍历 坑位的链表( 以这个坑位为首索引,通过next连接的各个子节点 )
如果该坑位或者坑位的子节点为空,则为其创建新的节点,形成链表形式;紧接着判断子节点数(算上坑位)是否>=7,即链表串元素>=7时,进行put会进行treeifyBin()进化成红黑树,不满足则break
如果需要put的这货在坑位的子节点中已经存在了,break
else {
logger.info("发生碰撞, 当前p.hash:" + p.hash + ",p.value:" + value + ",进入搜索next节点的循环");
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
logger.info("p.next为空,为其创建新的节点,p.next.hash:" + hash + ", p.next.value:" + value);
p.next = newNode(hash, key, value, null); // 将当前的节点的下一个节点指向新创建的节点
if (binCount >= TREEIFY_THRESHOLD - 1) // 只有>=7次迭代才会执行进化树结构
{
logger.info("binCount >= (TREEIFY_THRESHOLD - 1), binCount:" + binCount + ", (TREEIFY_THRESHOLD - 1):" + (TREEIFY_THRESHOLD - 1));
treeifyBin(tab, hash);
}
logger.info("跳出循环");
break;
}
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))){
logger.info("同一个对象");
break;
}
logger.info("p.next有值, p.next.hash:" + p.next.hash + ",p.next.value:" + p.next.value + ", 把当前p指针指向p.next");
p = e;
}
}
最后,put成功的话会count++,判断是否需要resize,结束put。
++modCount; //执行put操作的次数
logger.info("modCount:" + modCount);
if (++size > threshold) //已经存放元素的容量+1 与 扩容阀值进行对比
{
logger.info("++size > threshold, size:" + size + ", threshold:" + threshold);
resize();
}
3.2.1.3 判断是否需要扩容
3.2.1.1 When&How执行 resize()
3.2.1.2 When&How进行链式或者红黑树操作
3.3 get()
【JVM】-NO.113.JVM.1 -【JDK11 HashMap详解-0-全局-put】的更多相关文章
- 【JVM】-NO.113.JVM.1 -【JDK11 HashMap详解-4-resize()】
Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
- 【JVM】-NO.110.JVM.1 -【JDK11 HashMap详解】
Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
- 【JVM】-NO.114.JVM.1 -【JDK11 HashMap详解-3-put-treeifyBin()-AVL】
Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
- 【JVM】-NO.115.JVM.1 -【JDK11 HashMap详解-4-伸展树、B树】
.Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
- 【JVM】-NO.116.JVM.1 -【JDK11 HashMap详解-5-红黑树】
Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
- 【JVM】-NO.111.JVM.1 -【JDK11 HashMap详解-1-hash()剖析】
Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
- 【JVM】-NO.112.JVM.2 -【JDK11 HashMap详解-2-tab[i = (n - 1) & hash])剖析】
Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
- java面试题之----JVM架构和GC垃圾回收机制详解
JVM架构和GC垃圾回收机制详解 jvm,jre,jdk三者之间的关系 JRE (Java Run Environment):JRE包含了java底层的类库,该类库是由c/c++编写实现的 JDK ( ...
- 【转】 java中HashMap详解
原文网址:http://blog.csdn.net/caihaijiang/article/details/6280251 java中HashMap详解 HashMap 和 HashSet 是 Jav ...
随机推荐
- Python的浮点数损失精度问题
本篇讨论的现象可以从下面这段脚本体现出来: >>> x = 0.0 >>> for i in range(10): x += 0.1 print(x) 0.1 0. ...
- 2018铁三测评题write以及一些想送给你们的话
一..前言 此文献给实验室的萌新们,以及刚刚接触CTF的同学们,希望能对你们的成长起到一些帮助. 二.关于CTF 可能你已经接触过CTF或者对它有所了解,这里我再简单介绍一下. 1.什么是CTF? C ...
- [转]decorators.xml的用法
原文地址:https://blog.csdn.net/laozhuxiao/article/details/54342121 简介: sitemesh应用Decorator模式,用filter截取re ...
- 【转载】最强NLP预训练模型!谷歌BERT横扫11项NLP任务记录
本文介绍了一种新的语言表征模型 BERT--来自 Transformer 的双向编码器表征.与最近的语言表征模型不同,BERT 旨在基于所有层的左.右语境来预训练深度双向表征.BERT 是首个在大批句 ...
- 解决wps/office 1-2自动转换1月2日,用样式解决此问题
添加样式: td{mso-number-format:"\@"; }
- Android 引用外部字体
在Android中,加载外部字体是非常容易的! 步骤如下: 1. 创建新的Android工程: 2. 在工程下的assets文件夹下新建名字为fonts的文件夹(名字可以任意选取),把所有的外部字体文 ...
- 【转】WPF自定义控件与样式(13)-自定义窗体Window & 自适应内容大小消息框MessageBox
一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等. 本文主要内容: 自定义Window窗体样式: 基于自定义窗体实现自定义MessageB ...
- myeclipse及Eclipse中.classpath、.project、.settings、.mymetadata(myeclipse特有)介绍
引言 今天在创建java项目的时候遇到了很多的错误,在解决的过程中遇到了一些根本不知道什么作用的文件,然后按照网上的一些做法可以将问题解决,但是这也说明我们在学习的时候很多基础和细节的地方是我们薄弱的 ...
- 使用100%面向过程的思维方式来写java程序
1.java是强制写class关键字的语言,不能有独立的函数游离在类外出现在文件中,这和python c++ 都不同,后面的都可以单独在类外写函数,所以java被称为是纯面向对象的语言,py和c++都 ...
- scala-maven-plugin excludes
Hello, every one. I have a problem to add excludes to scala-maven-plugin. There are two scala files: ...