深入理解HashMap(三): 关键源码逐行分析之构造函数
前言
系列文章目录
上一篇我们说明了HashMap的hash算法, 说到HashMap在构造时会自动将table设为2的整数次幂.
本篇我们就来聊聊HashMap的构造函数.
本文的源码基于 jdk8 版本.
构造函数
HashMap 共有四个构造函数
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {// 默认初始大小 16static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16// 默认负载因子 0.75static final float DEFAULT_LOAD_FACTOR = 0.75f;final float loadFactor;/*** The next size value at which to resize (capacity * load factor).** @serial*/// (The javadoc description is true upon serialization.// Additionally, if the table array has not been allocated, this// field holds the initial array capacity, or zero signifying// DEFAULT_INITIAL_CAPACITY.)int threshold;transient Node<K,V>[] table;// 没有指定时, 使用默认值// 即默认初始大小16, 默认负载因子 0.75public HashMap() {this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted}// 指定初始大小, 但使用默认负载因子// 注意这里其实是调用了另一个构造函数public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);}// 指定初始大小和负载因子public HashMap(int initialCapacity, float loadFactor) {if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);this.loadFactor = loadFactor;this.threshold = tableSizeFor(initialCapacity);}// 利用已经存在的map创建HashMappublic HashMap(Map<? extends K, ? extends V> m) {this.loadFactor = DEFAULT_LOAD_FACTOR;putMapEntries(m, false);}}
不知道大家发现了没有, 即使我们在构造函数中指定了initialCapacity
, 这个值也只被用来计算 threshold
this.threshold = tableSizeFor(initialCapacity);
而 threshold
这个值在初始化table时, 就代表了数组的初始大小, 这个我们到后面用到的时候讲.
我们先来看看tableSizeFor
函数干了什么事:
/*** Returns a power of two size for the given target capacity.*/
static final int tableSizeFor(int cap) {int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
tableSizeFor这个方法用于找到大于等于initialCapacity的最小的2的幂, 这个算法还是很精妙的, 这里我稍微解释一下:
我们知道, 当一个32位整数不为0时, 32bit中至少有一个位置为1, 上面5个移位操作的目的在于, 将 从最高位的1
开始, 一直到最低位的所有bit 全部设为1, 最后再加1(注意, 一开始是先cap-1
的), 则得到的数就是大于等于initialCapacity的最小的2的幂. 读者自己找一个数算一下就明白了, 也可以参照这一篇博客.
最后我们来看最后一个构造函数, 它调用了 putMapEntries
方法:
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {int s = m.size();if (s > 0) {if (table == null) { // pre-sizefloat ft = ((float)s / loadFactor) + 1.0F;int t = ((ft < (float)MAXIMUM_CAPACITY) ?(int)ft : MAXIMUM_CAPACITY);if (t > threshold)threshold = tableSizeFor(t);}else if (s > threshold)resize();for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {K key = e.getKey();V value = e.getValue();putVal(hash(key), key, value, false, evict);}}
}
我们知道, 当使用构造函数HashMap(Map<? extends K, ? extends V> m)
时, 我们并没有为 table
赋值, 所以, table
值一定为null
, 我们先根据传入Map的大小计算 threshold
值, 然后判断需不需要扩容, 最后调用 putVal
方法将传入的Map插入table中.
resize
和 putVal
方法我们以后再细讲.
总结
通过上面对四个构造函数的分析我们发现, 除了最后一个构造函数, 其他三个函数:
HashMap()
HashMap(int initialCapacity)
HashMap(int initialCapacity, float loadFactor)
的调用中, 最多只牵涉到HashMap的两个Field loadFactor
, threshold
, 而并不牵涉到 table
变量.
这说明HashMap中, table
的初始化或者使用不是在构造函数中进行的, 而是在实际用到的时候, 事实上, 它是在HashMap扩容的时候实现的, 即resize
函数, 我们在下一篇文章中讨论.
(完)
下一篇: 深入理解HashMap(四): 关键源码逐行分析之resize
查看更多系列文章:系列文章目录
深入理解HashMap(三): 关键源码逐行分析之构造函数相关推荐
- HashMap源码逐行分析
目录 1.数据结构 1.Node 2.LinkedHashMap$Entry 3.TreeNode 1.类结构 2.root() 3.moveRootToFront() 4.find() 5.tieB ...
- HashMap,ArrayMap,SparseArray 源码角度分析,Android中的数据结构你该如何去选择?
table = newTab; 可以看到当我们的table数组存储的节点值大于threshold时,会按我们的当前数组大小的两倍生成一个新的数组,并把旧数组上的数据复制到新数组上这就是我们的HashM ...
- 2019最新妙味课堂jquery源码分析 jquery源码逐行分析全套
视频教程一共58课有10+G,解析的jquery版本未2.0版[娘的,我现在都还在用1.8,他们13年就已经在用2.0了!看来我是落后了!].本视频学习的好处不比那些实战的差!具体自己去感悟! 下载地 ...
- iris流程图_GitHub - LeoIris/vue: vue源码逐行注释分析+40多m的vue源码程序流程图思维导图 (diff部分待后续更新)...
vue源码业余时间差不多看了一年,以前在网上找帖子,发现很多帖子很零散,都是一部分一部分说,断章的很多,所以自己下定决定一行行看,经过自己坚持与努力,现在基本看完了 .这个vue源码逐行分析,我基本每 ...
- Vue 合同模板_vue源码逐行注释分析+40多m的vue源码程序流程图思维导图
vue源码业余时间差不多看了一年,以前在网上找帖子,发现很多帖子很零散,都是一部分一部分说,断章的很多,所以自己下定决定一行行看,经过自己坚持与努力,现在基本看完了,差ddf那部分,因为考虑到自己要换 ...
- 推荐 7 个 Vue2、Vue3 源码解密分析的开源项目
大家好,我是你们的 猫哥,那个不喜欢吃鱼.又不喜欢喵 的超级猫 ~ 1. 为什么要学习源码 ? 阅读优秀的代码的目的是让我们能够写出优秀的代码. 不给自己设限,不要让你周围人的技术上限成为你的上限.其 ...
- HashMap 源码深度分析
HashMap 源码分析 在Map集合中, HashMap 则是最具有代表性的,也是我们最常使用到的 Map 集合.由于 HashMap 底层涉及了很多的知识点,可以比较好的考察一个人的Java的基本 ...
- HashMap 底层源码细致分析
JDK集合HashMap 底层源码细致分析 前言 提示:对于初始 HashMap 的小伙伴来说,不推荐直接硬啃,建议先看一下如下几个视频教程之后再回头好好理解.(一遍看不懂则反复看,一小块一小块的找对 ...
- Android技术栈--HashMap和ArrayMap源码解析
1 总览 WARNING!!:本文字数较多,内容较为完整并且部分内容难度较大,阅读本文需要较长时间,建议读者分段并耐心阅读. 本文会对 Android 中常用的数据结构进行源码解析,包括 HashMa ...
最新文章
- reid笔记 yolov5 deepsort
- mysql文献综述_文献综述随笔(二十)
- 摘成功道路上容易被忽视的5项技能
- task_struct结构体查找
- 相量除法能用计算机吗,电路相量的加减乘除运算
- 贪吃蛇c语言代码高难,100多行代码的《贪吃蛇》
- [恢]hdu 2062
- 从Java源代码生成类图
- Qt 读写 txt 文件
- Google Hacker
- wps怎么做文档分享
- 安卓linux获取最高权限获取,安卓root是什么意思(获取手机最高权限)
- 利用-flat.vmdk文件恢复虚拟机
- 服务器托管如何选择双线三线的idc服务商
- 串口通信的隔离传输方案记录
- kubernetes高可用集群web页面部署
- Microsoft visio 2010之简单使用
- 第二阶段>>>数据库/SQL/SSM/JDBC/核心总结
- RF MEMS开关时代将开启-30?
- 流量监控服务器应该位置在哪里,搭建cacti流量监控服务器.pdf