美文网首页
记录读源码-HashMap(1)

记录读源码-HashMap(1)

作者: 浅笑丨无痕 | 来源:发表于2018-04-01 23:51 被阅读0次

    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.判断键值对数量是否大于阈值,大于的话则进行扩容操作

     

    相关文章

      网友评论

          本文标题:记录读源码-HashMap(1)

          本文链接:https://www.haomeiwen.com/subject/phizcftx.html