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
1391

被折叠的 条评论
为什么被折叠?



