记录小白实习生的HashMap源码 put元素 的学习和一些疑问
首先看HashMap存储结构
transient Node<K,V>[] table; static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next; Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
/*
……
*/
}
我对存储结构的理解
初始化一个长度为16的Node数组,数组中每一个元素是一个Node构成的单链表,好像是大家说的桶?当桶中Node结点长度(链表长度)大于等于TREEIFY_THRESHOLD (8)时 单链表改为 树(红黑树?现在还一点不了解,知道个名字) 存储 ,当桶中Node结点长度(链表长度)小于等于UNTREEIFY_THRESHOLD (6)时,树形结构转换为单链表存储
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
putVal 参数 分别为 key的哈希值,key值,value值,onlyIfAbsent true表示只有在该key对应原来的value为null的时候才插入,也就是说如果value之前存在了,就不会被新put的元素覆盖,false相反,evict //evict if false, the table is in creation mode. 这个是源码中的注释,true的话就不是creation mode?看园里大佬zju_jzb的博说
用于LinkedHashMap中的尾部操作,这里没有实际意义 传送门 https://www.cnblogs.com/jzb-blog/p/6637823.html
new HashMap<>()进行put时
先对table=null 和 table.length = 0 的情况进行处理 执行resize方法默认构造一个长为16的Node数组
再根据hash值 (table.length - 1) & hash 计算出put 的桶的下标
若该元素为空 newNode(hash, key, value, null); 创建一个单链表的“头”结点
若该元素不为空
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
如果第一个元素key与put的key相同时,将第一个元素引用赋值给要put的新结点e
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
如果第一个元素 是 TreeNode类型时,说明已转换为树形结构存储,插入到树中
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;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
如果第一个元素与要put元素不同,而且此时也仍是单链表结构存储的话,遍历链表。找到后又分三种情况
第一种情况时,插入后链表长度达到8,需要转化为树形结构。
第二种情况时,插入后链表长度小于8,仍然是链表存储。
第三种情况时,链表中遍历到相同key值的结点,获得该结点的引用
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
对已存在key的value进行覆盖 返回put之前key所对应的值
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
modCount是对HashMap结构改变次数的记录(插入删除)
若put一个元素后 需要对Node[] table进行扩容 就扩容
putVal 方法走到这里时已经说明 HashMap中不存在和要put的Key相同的Key 返回null
afterNodeInsertion方法还不了解
看了一天了 感觉对HashMap的大致结构有一定的了解了 但是还有很多疑问
记录小白实习生的HashMap源码 put元素 的学习和一些疑问的更多相关文章
- hashMap源码学习记录
hashMap作为java开发面试最常考的一个题目之一,有必要花时间去阅读源码,了解底层实现原理. 首先,让我们看看hashMap这个类有哪些属性 // hashMap初始数组容量 static fi ...
- Java中的HashMap源码记录以及并发环境的几个问题
HashMap源码简单分析: 1 一切需要从HashMap属性字段说起: /** The default initial capacity - MUST be a power of two. 初始容量 ...
- HashMap源码分析
最近一直特别忙,好不容易闲下来了.准备把HashMap的知识总结一下,很久以前看过HashMap源码.一直想把集合类的知识都总结一下,加深自己的基础.我觉的java的集合类特别重要,能够深刻理解和应用 ...
- HashMap源码解读(转)
http://www.360doc.com/content/10/1214/22/573136_78188909.shtml 最近朋友推荐的一个很好的工作,又是面了2轮没通过,已经是好几次朋友内推没过 ...
- 自学Java HashMap源码
自学Java HashMap源码 参考:http://zhangshixi.iteye.com/blog/672697 HashMap概述 HashMap是基于哈希表的Map接口的非同步实现.此实现提 ...
- HashMap源码分析(一)
前言:相信不管在生产过程中还是面试过程中,HashMap出现的几率都非常的大,因此有必要对其源码进行分析,但要注意的是jdk1.8对HashMap进行了大量的优化,因此笔者会根据不同版本对HashMa ...
- JDK1.8 HashMap源码分析
一.HashMap概述 在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的节点都存储在一个链表里.但是当位于一个桶中的元素较多,即hash值相等的元素较多时 ...
- 基于JDK1.8版本的hashmap源码笔记(二)
这一篇是接着上一篇写的, 上一篇的地址是:基于JDK1.8版本的hashmap源码分析(一) /** * 返回boolean类型的值,当集合中包含key的键值,就返回true,否则就返 ...
- HashMap源码解读(JDK1.7)
哈希表(hash table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出 ...
随机推荐
- 将TUM数据集的RGB-D数据集转化为klg格式
1.在github上下载代码png_to_klg git clone https://github.com/HTLife/png_to_klg 2.将png_to_klg目录下的associate.p ...
- wps excel
ET.Application etApp;ET.workbook etbook;ET.Worksheet etsheet ;ET.Range etrange;//获取工作表表格etApp = new ...
- Cocostudio 1.4 实现的Demo程序源码
开发环境是CocoStudio 1.4 + Cocos2dx 2.2.3 把项目文件放到Cocos2dx下的projects文件夹下就可以执行了 压缩包里面包括了 源码 和资源文件 1.DemoSh ...
- 部署activiti 5.15.1的Activiti Explorer
1.从官网下载activiti包,将其中的activiti-explorer.war文件拷贝到tomcat的webapps目录下: 2.将mysql驱动包文件mysql-connector-java- ...
- DNS服务器地址汇总
如果修改DNS服务器地址就可以访问google等服务,你还等什么?使用免费DNS解析服务除了去掉了运营商的各种广告,还有个最大的好处就是不会重定向或者过滤用户所访问的地址,这样就防止了很多网站被电信. ...
- Java中级开发工程师知识点归纳
(一)Java 1.接口和抽象类的区别 ①抽象类里可以有构造方法,而接口内不能有构造方法. ②抽象类中可以有普通成员变量,而接口中不能有普通成员变量. ③抽象类中可以包含非抽象的普通方法,而接口中所有 ...
- mysql循环插入千万级数据
mysql使用存储过程循环插入大量数据,简单的一条条循环插入,效率会很低,需要考虑批量插入. 测试准备: 1.建表: CREATE TABLE `mysql_genarate` ( `id` ) NO ...
- json,pickle,shelve模块,xml处理模块
常用模块学习—序列化模块详解 什么叫序列化? 序列化是指把内存里的数据类型转变成字符串,以使其能存储到硬盘或通过网络传输到远程,因为硬盘或网络传输时只能接受bytes. 为什么要序列化? 你打游戏过程 ...
- OPPO R6007在哪里打开usb调试模式的完美流程
当我们使用Pc接通安卓手机的时候,如果手机没有开启USB开发者调试模式,Pc则无法成功读到我们的手机,遇到此种情况我们需要想方设法将手机的USB开发者调试模式打开,以下内容我们讲解OPPO R6007 ...
- 3、谈谈 Java NIO
在 JDK1.4 之后,为了提高 Java IO 的效率,Java 提供了一套 New IO (NIO),之所以称之为 New,原因在于它相对于之前的 IO 类库是新增的.此外,旧的 IO 类库提供的 ...