记录读源码-HashMap(1)

HashMa源码很久没看了,有点忘记,还是得做点笔记

1.HashMap中定义的常量

1.默认容量

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

2.最大容量

static final int MAXIMUM_CAPACITY = 1 << 30;

3.装载因子默认值

装载因子用来衡量HashMap满的程度,loadFactor的为0.75f。

static final float DEFAULT_LOAD_FACTOR = 0.75f;

4.由链表转换成树的阈值

由链表转换成树的阈值,当桶中数量超过TREEIFY_THRESHOLD时使用树来代替链表。默认值是8

static final int TREEIFY_THRESHOLD= 8;

5.由树转换成链表的阈值

当执行resize操作时,当桶中数量少于UNTREEIFY_THRESHOLD时使用链表来代替树。默认值是6

static final int UNTREEIFY_THRESHOLD = 6;

6.hash表开始树化阈值

static final int MIN_TREEIFY_CAPACITY = 64;


2.HashMap中定义的变量

transient Node[] table;//节点表,后面统称为(>﹏<)

transient Set>entrySet;

transient int size;// HashMap中存放KV的数量(为链表和树中的KV的总和)

transient int modCount;// 修改的次数, 主要用于迭代的快速失败

int threshold;// threshold表示当HashMap的size大于threshold时会执行resize操作,threshold=capacity*loadFactor

final float loadFactor; // 装载因子用来衡量HashMap满的程度,loadFactor的默认值为0.75f。计算HashMap的实时装载因子的方法为:size/capacity


3. 内部数据结构Node

HashMap采用内部静态类Node来保存键值对, Node类定义如下,其中hash为key的hash值。


4.hash()

static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}

从上代码可以看到key的hash值的计算方法:将key的hashCode右移16位后,与自己做异或,其实就是低16位与高16位异或作为key的最终hash值,目的是加大低位的随机性,而且混合后的低位掺杂了高位的部分特征,这样高位的信息也被变相保留下来。例如:


hash转换图

5.获取数据方法get

public V get(Object key) { Node e; return (e = getNode(hash(key), key)) == null ? null : e.value;}

get方法调用getNode方法获取对应的value值, getNode方法如下:


getNode方法

查找的核心逻辑是封装在 getNode 方法中,源码还是可以看懂的,我稍微加了注释。第一步是确定桶位置,其实现代码如下:

first = tab[(n - 1) & hash]

这里可以解释HashMap 中桶数组的大小 length 为什么总是2的幂,此时,(n- 1) & hash 等价于对 length 取余。但取余的计算效率没有位运算高,所以(n - 1) & hash也是一个小的优化,例如下图:


桶索引的计算图

5.添加数据put方法

通过前面get方法的分析,大概可以知道插入对象的流程吧,首先肯定是先定位要插入的键值对属于哪个桶,定位到桶后,再判断桶是否为空。如果为空,则将键值对存入即可。如果不为空,则需将键值对接在链表最后一个位置,或者更新键值对。其实,HashMap 的插入流程比上面描述复杂得多,首先是HashMap的扩容,其次是树化过程,这里我们先看一下插入源码吧:


插入操作的入口方法是put(K,V),但核心逻辑在V putVal(int, K, V, boolean, boolean)方法中。putVal 方法总结起来就是如下过程:

1.当桶数组 table 为空时,通过扩容的方式初始化 table

2.查找要插入的键值对是否已经存在,存在的话根据条件判断是否用新值替换旧值

3.如果不存在,则将键值对链入链表中,并根据链表长度决定是否将链表转为红黑树

4.判断键值对数量是否大于阈值,大于的话则进行扩容操作

 

全部评论

相关推荐

10-06 12:46
门头沟学院 Java
跨考小白:定时任务启动
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务