Java HashMap源码解析 不是COPY

HashMap是Java中一个很常用的类

它继承AbstractMap<K,V> ,实现Map<K,V>, Cloneable, Serializable 接口

首先从HashMap 的构造器说起:

总共有四种构造器

第一种:public HashMap() 它执行的时候只是把默认的负载因子 赋给loadFactor 变量(哈希表的装载因子。)

第二种:public HashMap(int initialCapacity) 它相当于调用了this(initialCapacity, DEFAULT_LOAD_FACTOR);这个构造器(请看第三种)

第三种:public HashMap(int initialCapacity, float loadFactor) 

首先判断initialCapacity 初始容器大小是否 大于等于0

initialCapacity  是否小于最大值(1 << 30)

还要保证你自定义的负载因子是不是大于等于0, Float.isNaN(loadFactor) 是不是false

说道这里就要说Float.isNaN 方法了,这个方法:true如果该对象表示的值为NaN;false否则。也就是判断是否合法

然后把负载因子赋给hashmap中

把tableSizeFor(initialCapacity) 赋给threshold(要调整大小的下一个大小值(容量*装载因子)。)

我们这里要看tableSizeFor 方法了,这个方法的作用就是 因为capacity 必须是2的幂,hashmap的结构(数组+链表)分布的更平均,不会说大部分在同一个数组的某一个中,

先减一 然后通过位运算 有右移,然后判断n 是否小于0,如果大于等于0,就要看他是不是小于最大值,如果是,把刚开始减掉的一 加回来

这里可能就要有人问,为什么要减一呢然后之后又加回来,还有为什么能够平均分布?

由于HashMap的capacity都是2的幂,因此这个方法用于找到大于等于initialCapacity的最小的2的幂(initialCapacity如果就是2的幂,则返回的还是这个数)。

为什么减一?

答:为了防止,cap已经是2的幂。如果cap已经是2的幂, 又没有执行这个减1操作,则执行完后面的几条无符号右移操作之后,返回的capacity将是这个cap的2倍

为什么能平均分布?

答:其实这个和cap是2的幂,还有hash方法有关,后面说

然后我们把tableSizeFor 结果赋给threshold 但是我们从threshold的注释 翻译得到的内容,应该是容量乘以装载因子

本来以为是有bug的,然后后来看了一下还有看了别的文档 发现不是

答:在构造方法中,并没有对table这个成员变量进行初始化,table的初始化被推迟到了put方法中,在put方法中会对threshold重新计算,所以这个没有


好了,接着昨天继续:

今天开始附图,原本不想附图,现在的附图太费时

第四种构造器:public HashMap(Map<? extends K, ? extends V> m) 把默认的装载因子(也就是之前说的负载因子) 赋给loadFactor

然后执行putMapEntries(m, false);

putMapEntries方法(这里为了方便大家跟着思路走,说下这个代码):

1.看map的size 是否大于0,如果不大于0,相当于第一种构造器

2.然后判断table 是否为空,如果是null,float ft = (size / loadFactor) + 1.0F,然后ft和最大值(1 << 30)比较,取小的赋给t。如果t 大于threshold

,调用tableSizeFor得到大于t的最小2的幂

如果不是null,还size > threshold,就重新生成 调用 resize();3.然后开始遍历map (m.entrySet()),然后每一个k,v 调用putVal方法


在这里说下putVal方法:反正到时候说put也是调这个方法

1.看table 是不是空,或者length 等于0,如果是的话,resize()重新排序之后把值赋给tab,然后把length 赋给n

2.用i = (n - 1) & hash计算在数组的那个部分,然后把这个部分赋给p(Node<K,V>),如果p是null,就newNode 然后赋给tab[i]

2.2 里面本来就有Entry(链表),hash赋给p.hash,当前这个Entry的key 赋给k(K),和这个的Key是否相等 或者(||) (key != null && key.equals(k))

这个为什么要分开(||)呢,因为 = 比的是地址,后面还有一个equals 比较,如果相等 把e = p(数组的相应部分)

   2.3  else if p 属于TreeNode(这是什么呢,是在Entry大于默认的8个的时候,会把链表转为 Tree),这个我后面再说

2.4 else 开始遍历,既然当前这个Entry不符合,那就p.next 非空,如果非空,就newNode赋到p.next,如果entry个数大于8个,就调用treeifyBin方法,

3 . 最后就是修改次数加一(++modCount)

 if (++size > threshold)
            resize();

然后说到这就要说一下table变量(transient Node<K,V>[] table;)之后继续补充




结尾:-------

到这里,基本应该懂了吧,能看到这里的人,我只希望大家能也多分享一些(不论是技术还是任何其他事),感恩前人。有什么问题,请评论或者给我发私信,谢谢大家

—— xgf



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孔明兴汉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值