HashMap
HashMap的继承体系
核心属性+构造方法
状态属性:
- DEFAULT_INITIAL_CAPACITY = 1 << 4; == 16 默认的初始长度
- MAXIMUN_CAPACITY = 1 << 30; Hash表的最大长度,其是由JVM决定的
- DEFAULT_LOAD_FACTORY = 0.75f ; 默认的负载因子大小
- TREEIFY_THRESHOLD = 8; 树化的最小链长
- UNTREEIFY_THRESHOLD = 6 ;树降级为链的链长
- MIN_TREEIFY_CAPACITY = 64 ; 链表转化为树时,数组的最小长度。
成员属性
- Node继承了Map.entry类,
- Node<K,V>[] table; Hash表的数组
- size; Hash表的长度
- modCount; 记录Hash表的修改次数,包括插入、删除等(替换不会计数)。
- loadFactory ;负载因子
- threshold ; 扩容阈值(开始时,其数值为tableSizeFor(initCapacity)),但是由于延迟初始化,其并没有真的在内存中分配空间,在后面初始化以后就会变成capacity*loadFactory。
- tableSizeFor()函数的目的就是把自定义输入的数值转化成最小的2的n次幂,通过对initCapacity-1 ,不断(对右移1,2,4,8,16位)进行位与操作,得到2的n次幂。
- 必须是initCapacity,否则在8、16、32等输入2的n次幂的情况下会扩大2倍,变成16、32、64,而不是8、16、32。
putVal()方法
- putVal一共分为4种情况。
Hash扰动处理
- 将Hash的高16位与低16位进行异或处理,得到扰动后的Hash数值
- 扰动后的Hash数值(hash),再将其和(n-1)进行位与操作,得到散列表中的index,在2进制环境下也就是对hash进行取模操作。
延迟初始化,
- 如果table为空或者,table的长度为0,这时才初始化数组,以节省内存开销。
第一种情况:Hash数组中没有元素
- 找到数组的指定索引位置,插入新节点
- 这时会跳出判断,++modCount,
第二种情况:Hash数组中有元素并正是我们要找的
- e表示找到了一个我们要找的相同的节点。
- 更改成为新值,同时返回旧值。
第三种情况:Hash数组中有元素,不是我们要找的找到,并且其数据结构是红黑树
- 判断数组中的第一个节点是否是红黑树,如果是,则执行红黑树的插入操作。返回e。
- 如果e不为空,则表示原来有值,修改成为新值,返回旧值。
第四种情况:Hash数组中有元素,不是我们要找的找到,并且其数据结构是链表
- 链表也有三种情况,
- 遍历当前链表,找到节点
- 遍历当前链表,没有找到节点,但是也没有超过树化阈值
这里用binCount来记录遍历次数,因为遍历是从0开始的,所以当binCount>=TREEIFY_THRESHOLD-1就可以了认为是可以树化了。
- 遍历当前链表,没有找到节点,达到树化阈值(见
- 遍历当前链表,找到节点