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

深入理解 HashMap put 方法(JDK 8逐行剖析) 前言注意:我们今天所有的一切都是基于 JDK 8,JDK 8 的实现和 JDK 7 有重大区别。前面我们分析了 hashCode 和 hash 算法的原理,其实都是为我们解析 HashMap 做铺垫,因为 HashMap 确实比较复杂(如果你每一行代码都看的话,每个位移都纠结的话),虽然总的来说,HashMap 不过是 Node 数组加 链表和红黑树。但是里面的细节确是无比的优雅和有趣。楼主为什么选 阅读详情

上一篇我们说明了HashMap的hash算法, 说到HashMap在构造时会自动将table设为2的整数次幂.

本篇我们就来聊聊HashMap的构造函数.

本文的源码基于 jdk8 版本.

构造函数

HashMap 共有四个构造函数

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {

    // 默认初始大小 16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    // 默认负载因子 0.75
    static 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.75
    public 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创建HashMap
    public 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-size
            float 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方法解析 前言 上一篇讲了put方法,在put方法的最后,我们需要检查容量是否超过阈值,,如果越界需要进行扩容。resize方法就是对hashmap进行扩容。那么hashmap的扩容需要哪些步骤呢? 假如我是设计的作者,我会先创建一个更大容量的数组,然后遍历原table,重新计算所有的节点的hash值对应的下标,然后将节点转移到新table中。那么这其中存在这几个问题: 什么时候进行resize操作? 扩... 阅读详情

相关推荐

永磁同步电机高效标定技术:从理论到实践的全面解析

本文全面解析永磁同步电机高效标定技术,从MTPA标定、弱磁区优化到初始角校准,详细介绍了理论原理和实战技巧。针对新能源汽车应用场景,提供标定系统集成要点和AI辅助优化方案,帮助工程师提升电机性能标定效率与精度。

weixin_28452161的博客 501

HashMap(二):构造函数

一、HashMap的四个构造函数: 1、 public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + ...

GOODNESS_的博客 1520

Zemax实战:手把手教你设计一个40倍、NA0.65的显微物镜(附初始结构文件)

本文详细介绍了使用Zemax设计40倍、NA0.65显微物镜的完整流程,从初始结构搭建到优化验证,涵盖光学设计的关键技巧和实战经验。通过分阶段优化策略和公差分析,帮助工程师快速掌握高倍显微物镜的设计方法,提升光学系统性能。

weixin_42525369的博客 342

HashMap构造函数有哪些

hashMap有4个构造函数: public HashMap(int initialCapacity, float loadFactor) public HashMap(int initialCapacity) public HashMap() public HashMap(Map<? extends K, ? extends V> m...

码瘾的空间 760

Tomcat启动过程中cmd窗口(控制台)中文乱码的问题

tomcat启动时,启动过程日志中文乱码的处理方法。

寻码启示 1942

解决nfs挂载根文件系统报错:VFS: Unable to mount root fs via NFS, trying floppy.

解决nfs挂载根文件系统报错的问题:VFS: Unable to mount root fs via NFS, trying floppy.

汉尼拔勇闯天涯 4573

HashMap的深入剖析

HashMap的深入剖析 HashMap的深入剖析 前沿 一、HashMap的顶部注释 二、HashMap的依赖关系 三、HashMap的数据结构 五、HashMap 构造函数 六、put()方法 6.1、 hash(Object key) 6.2、putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) 6....

River的博客 540

Mac 不小心断开移动硬盘导致磁盘无法读取和加载(顺利解决!)

Mac移动硬盘不正确断开连接导致无法读取加载的解决方法,亲测有效

Robin_Pi的博客 1万+

Java 集合系列之 HashMap详细介绍(源码解析)和使用示例

概要 这一章,我们对HashMap进行学习。 我们先对HashMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashMap。内容包括: 第1部分 HashMap介绍 第2部分 HashMap数据结构 第3部分 HashMap源码解析(基于JDK1.6.0_45)     第3.1部分 HashMap的“拉链法”相关内容     第3.2部分 HashMa

fivehundredyearsago的博客 940

[DDR5]DDR5 SDRAM 状态图 和 基本功能

DDR5 SDRAM的读写操作以突发(burst mode)为导向,从选定的位置开始,并按照编程顺序进行16个突发长度或8个“切片”突发长度的操作。从ACTIVATE命令开始,然后是读取或写入命令。

串起点滴 1200

Halcon实例转OpenCVSharp--粘连物体分割与计数(基于距离变换+分水岭算法) (附源码)

本文作者Color Space,文章未经作者允许禁止转载! 本文将介绍OpenCVSharp应用实例--粘连物体分割与计数(基于距离变换+分水岭算法)! 本文使用的OpenCVSharp版本---》v4.5.3。 实现目标:将下图中的硬币分割出来并计算数量。 预期效果图: ...

Color Space的博客 2419

Java 基础 ——HashMap构造、PUT、GET

文章目录Java 基础 —— HashMap之图解HashMap Java 基础 —— HashMap之图解HashMap HashMap的数据结构包括了初始数组、链表、红黑树; 插入数据的时候使用key%size来进行插入数据; 当两个或者两个以上的key的key相同,且key值不同的时候(即%【取余】发生冲突,就会挂在数组初始化位置的链表后) 当某个节点后出现过多的链表节点的时候,就会换成红...

Genius only means hard-working all one's life. 1187

【FPGA教程案例96】控制案例1——基于FPGA的自适应PID控制器verilog实现

首先,我们复习下课程传统的PID控制器,往往存在调整速度较慢,超调过大等缺点。为了解决这个问题,在实际的PID应用中,需要实时的调整PID的控制参数,以达到加快PID控制速度,降低超调等目标。目前,自适应PID控制器,一般有误差反馈调整,模糊控制器调整,神经网络调整。其中模糊控制器调整,神经网络调整较为复杂,在入门案例中不做介绍,在本课程中,我们将实现相对略微简单点的基于误差反馈调整的自适应PID控制器。

FPGA/MATLAB学习教程/源码/项目合作开发 6201

java hashmap 构造函数_深入理解HashMap(三): 关键源码逐行分析之构造函数

前言上一篇我们说明了HashMap的hash算法, 说到HashMap在构造时会自动将table设为2的整数次幂.本篇我们就来聊聊HashMap的构造函数.本文的源码基于 jdk8 版本.构造函数HashMap 共有四个构造函数public class HashMap extends AbstractMap implements Map, Cloneable, Serializable {// 默...

weixin_39913117的博客 485

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

深入理解HashMap(五): 关键源码逐行分析之put

上一篇我们讨论了HashMap的扩容操作, 提到扩容操作发生在table的初始化或者table大小超过threshold后,而这两个条件的触发基本上就发生在put操作中。 本篇我们就来聊聊HashMap的put操作。 本文的源码基于 jdk8 版本. put方法 HashMap 实现了Map接口, 因此必须要实现put方法: public V put(K key, V value) { return putVal(hash(key), key, value, false, true); /*

a092cc的专栏 260

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

JDK8 源码解读:HashMap-构造函数

JDK8 源码解读:HashMap-构造函数前言阅读计划构造函数以及相关方法HashMap(int initialCapacity, float loadFactor)tableSizeFor(int cap)HashMap(int initialCapacity)HashMap()HashMap(Map<? extends K, ? extends V> m)putMapEntries(Map<? extends K, ? extends V> m, boolean evict)

乐之终曲 843

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

HashMap源码解析,看这一篇就够了

有关HashMap中关键的源码解析讲到这里就差不多了,这里的源码除了get和remove方法,其它都是面试的常问的知识,还是 要慢慢一点一点的查看。记不住就自己debug一遍,这是Java中很基础的知识了。如果还想看哪些集合的源码可以在评论区说,我也会留意尽快的讲解。

weixin_56690415的博客 432

深入理解 HashMap

什么是 HashMap? ​ HashMap 是基于哈希表的 Map 接口是实现的。此实现提供所有可选操作,并允许使用 null 做为值(key)和键(value)。HashMap 不保证映射的顺序,特别是它不保证该顺序恒久不变。此实现假定哈希函数将元素适当的分布在各个桶之间,可作为基本操作(get 和 put)提供稳定的性能。在jdk1.7中的HashMap是基于数组+链表实现的,在jdk1....

Woo_home的博客 3万+

深入理解 HashMap 关键部分源码

本文围绕 HashMap 展开,先简介其基于哈希表的 Map 接口实现、线程不安全特性,以及 JDK1.8 前后数组 + 链表、引入红黑树的结构差异与扩容树化规则;再从源码层面,解读常量、字段含义,分析 4 个构造函数逻辑,详解 hash 计算原理(右移 16 位及异或原因),并深入剖析 put、resize、get 核心方法的执行流程,展现 HashMap 的底层工作机制。

2302_80352818的博客 1384

java HashMap 源码分析(深度讲解)

java 集合篇章——HashMap源码分析(非常详细)。

优快云 / 稀土掘金/ B站 / GitHub: Cyan_RA9 2559

深入理解HashMap(四): 关键源码逐行分析之resize扩容

上一篇我们说明了HashMap的构造函数, 谈到构造函数中并不会初始化table 变量, table 变量是在 resize过程中初始化的. 本篇我们就来聊聊HashMap的扩容: resize 本文的源码基于 jdk8 版本. resize resize用于以下两种情况之一 初始化table 在table大小超过threshold之后进行扩容 下面我们直接来对照源码分析: final Node<K,V>[] resize() { Node<K,V>[] oldTab =

a092cc的专栏 317

深入理解HashMap

深入剖析了HashMap的实现原理和源码

mazouri的博客 5353

HashMap源码分析(深入理解HashMap)

1. HashMap总览 1.1 hashmap底层储存结构图解 底层结构其实就是数组+链表+红黑树 1.2 HashMap类定义 先来看看HashMap的定义: public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {} 从中我们可以了解到: HashMap<K,V>:HashMap是以key-value形式存

PUPPET4的博客 5589

深入理解HashMap(一)hashmap所用算法、构造函数

前言 大二的时候学习过hashmap的结构以及算法,但是一直都是通过使用JDK自带的hashmap,一直没有深究hashmap是如何实现的。最近重新阅读了hashmap的源码,发现hashmap的实现远比我想象的难。于是打算记录一下自己学习hashmap 的过程,如果没有学过数据结构的同学可以先去学习一下数据结构,hash的基础我就不再讲了。(本文的代码都是基于JDK8实现的) 1、hash算法实...

BodyCoding 2335

深入理解HashMap(二): 关键源码逐行分析之hash算法

前面我们讨论了HashMap的结构, 接下来几篇我们从源码角度来看HashMap的实现细节. 本篇我们就来聊聊HashMap的hash算法 本文的源码基于 jdk8 版本. hash算法 上一篇文章我们提到, 为了利用数组索引进行快速查找, 我们需要先将 key值映射成数组下标. 因为数组的下标是有限的集合, 所以我们可以先通过hash算法将key映射成整数, 再将整数映射成有限的数组下标: Object -> int -> index 对于 Object -> int 部分, 使用的

a092cc的专栏 144
上一篇: 深入理解HashMap(二): 关键源码逐行分析之hash算法
下一篇: 深入理解HashMap(四): 关键源码逐行分析之resize扩容
a092cc
博客等级 码龄16年 20粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值