前言

系列文章目录

上一篇我们说明了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中.

resizeputVal 方法我们以后再细讲.

总结

通过上面对四个构造函数的分析我们发现, 除了最后一个构造函数, 其他三个函数:

HashMap()
HashMap(int initialCapacity)
HashMap(int initialCapacity, float loadFactor)

的调用中, 最多只牵涉到HashMap的两个Field loadFactor, threshold, 而并不牵涉到 table 变量.

这说明HashMap中, table的初始化或者使用不是在构造函数中进行的, 而是在实际用到的时候, 事实上, 它是在HashMap扩容的时候实现的, 即resize函数, 我们在下一篇文章中讨论.

(完)

下一篇: 深入理解HashMap(四): 关键源码逐行分析之resize

查看更多系列文章:系列文章目录

深入理解HashMap(三): 关键源码逐行分析之构造函数相关推荐

  1. HashMap源码逐行分析

    目录 1.数据结构 1.Node 2.LinkedHashMap$Entry 3.TreeNode 1.类结构 2.root() 3.moveRootToFront() 4.find() 5.tieB ...

  2. HashMap,ArrayMap,SparseArray 源码角度分析,Android中的数据结构你该如何去选择?

    table = newTab; 可以看到当我们的table数组存储的节点值大于threshold时,会按我们的当前数组大小的两倍生成一个新的数组,并把旧数组上的数据复制到新数组上这就是我们的HashM ...

  3. 2019最新妙味课堂jquery源码分析 jquery源码逐行分析全套

    视频教程一共58课有10+G,解析的jquery版本未2.0版[娘的,我现在都还在用1.8,他们13年就已经在用2.0了!看来我是落后了!].本视频学习的好处不比那些实战的差!具体自己去感悟! 下载地 ...

  4. iris流程图_GitHub - LeoIris/vue: vue源码逐行注释分析+40多m的vue源码程序流程图思维导图 (diff部分待后续更新)...

    vue源码业余时间差不多看了一年,以前在网上找帖子,发现很多帖子很零散,都是一部分一部分说,断章的很多,所以自己下定决定一行行看,经过自己坚持与努力,现在基本看完了 .这个vue源码逐行分析,我基本每 ...

  5. Vue 合同模板_vue源码逐行注释分析+40多m的vue源码程序流程图思维导图

    vue源码业余时间差不多看了一年,以前在网上找帖子,发现很多帖子很零散,都是一部分一部分说,断章的很多,所以自己下定决定一行行看,经过自己坚持与努力,现在基本看完了,差ddf那部分,因为考虑到自己要换 ...

  6. 推荐 7 个 Vue2、Vue3 源码解密分析的开源项目

    大家好,我是你们的 猫哥,那个不喜欢吃鱼.又不喜欢喵 的超级猫 ~ 1. 为什么要学习源码 ? 阅读优秀的代码的目的是让我们能够写出优秀的代码. 不给自己设限,不要让你周围人的技术上限成为你的上限.其 ...

  7. HashMap 源码深度分析

    HashMap 源码分析 在Map集合中, HashMap 则是最具有代表性的,也是我们最常使用到的 Map 集合.由于 HashMap 底层涉及了很多的知识点,可以比较好的考察一个人的Java的基本 ...

  8. HashMap 底层源码细致分析

    JDK集合HashMap 底层源码细致分析 前言 提示:对于初始 HashMap 的小伙伴来说,不推荐直接硬啃,建议先看一下如下几个视频教程之后再回头好好理解.(一遍看不懂则反复看,一小块一小块的找对 ...

  9. Android技术栈--HashMap和ArrayMap源码解析

    1 总览 WARNING!!:本文字数较多,内容较为完整并且部分内容难度较大,阅读本文需要较长时间,建议读者分段并耐心阅读. 本文会对 Android 中常用的数据结构进行源码解析,包括 HashMa ...

最新文章

  1. reid笔记 yolov5 deepsort
  2. mysql文献综述_文献综述随笔(二十)
  3. 摘成功道路上容易被忽视的5项技能
  4. task_struct结构体查找
  5. 相量除法能用计算机吗,电路相量的加减乘除运算
  6. 贪吃蛇c语言代码高难,100多行代码的《贪吃蛇》
  7. [恢]hdu 2062
  8. 从Java源代码生成类图
  9. Qt 读写 txt 文件
  10. Google Hacker
  11. wps怎么做文档分享
  12. 安卓linux获取最高权限获取,安卓root是什么意思(获取手机最高权限)
  13. 利用-flat.vmdk文件恢复虚拟机
  14. 服务器托管如何选择双线三线的idc服务商
  15. 串口通信的隔离传输方案记录
  16. kubernetes高可用集群web页面部署
  17. Microsoft visio 2010之简单使用
  18. 第二阶段>>>数据库/SQL/SSM/JDBC/核心总结
  19. RF MEMS开关时代将开启-30?
  20. 流量监控服务器应该位置在哪里,搭建cacti流量监控服务器.pdf

热门文章

  1. Gradient Boosting Decision Tree学习
  2. oracle 数据结构
  3. asp Upload
  4. gbk转utf-8 iconv 编码转换
  5. 微信公众号介绍_以及注册订阅号---微信公众号开发工作笔记0001
  6. APPCAN学习笔记006_创建第一个APPCAN应用
  7. 杭电5256 序列变换(LIS)
  8. 数据库的四个范式之间的区别
  9. MSG_NOSIGNAL
  10. 计算机英语中motherboard,计算机英语--Motherboard.doc