HashMap底层源码分析



零、总结

默认阈值:默认数组初始容量16 * 默认加载因子 0.75 = 12

超过阙值,引发数组扩容

hash值计算:key的hashCode 异或上 hashCode向right移动16位

如果一个key-value数据在数组x下标位置, 当这个数组扩容, 扩容完成之后, 这个key-value数据要重新散列, 要么还在x位置, 要么在旧数组长度 + x位置 因为 默认扩容机制是2倍

扩容 resize():小于最大值MAXIMUM_CAPACITY,大于等于DEFAULT_INITIAL_CAPACITY => 大于等于这个值的最小的2的幂值作为底层数组的长度

添加数据 putVal():添加key得位置为空(n - 1) & hash => hash值和数组长度取模;不为空:比较key;key不同:判断存储位置是树还是链表

链表长度超过8达到9个数据的时候会进行红黑树的转化(阈值是8)

当底层数组数为空,或者底层数组的长度 < MIN_TREEIFY_CAPACITY (初始值为64) 时,会选择扩容,而不是创建树;

退化链表 removeTreeNode()——移除元素 remove() 操作:当红黑树(根节点 / 根节点的左右结点 / 根节点的左结点的左结点)这四个结点有任意一个不存在, 就要由红黑树转化为链表

退化链表 removeTreeNode()——扩容resize() 操作:导致树拆成两部分,树中的结点过少,转化成链表。

链表转化为红黑树阈值是8, 红黑树转化为链表阈值是6原因:阙值一样, 添加和删除可能导致红黑树和链表间的反复转化


一、知识点

加载因子:
HashMap 的默认的加载因子: 0.75,用来限定阈值(用于控制 HashMap 的饱和度)
阈值 = 加载因子 * 数组长度
默认阈值: 16 * 0.75 = 12 (默认的初始容量:16)
在数组中存储元素数量,超过阈值,会引发数组扩容

Hash算法:
不是加密算法,不可逆
常见的哈希算法:MD4 、 MD5 、SHA1、SHA2、 SHA3

hashmap 的 hash值的计算:
// 不是哈希算法,仿照哈希算法思想
// 不够充分随机,但是速度快
// 要存储 key-value 数据,对 key 计算,得到一个 int 类型的 hash 值
// 如果kay值是null, 它对应的hash值是0
// 如果kay值不是null, 取key的hashCode 异或上  hashCode向right移动16位    
// hashCode 向低位移动16位, 让高位和低位取异或运算的目的, 就是希望高位和低位充分混合, 最终都能参加到取模预算中去 
(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);

手动计算HashMap的hash值举例:
String str = "study";
int hash = str.hashCode();
hash = hash ^ (hash >>> 16);
// 将哈希值映射到有限的索引范围内
int index = hash % 底层数组长度;

// hashCode(用于记录对象的位置)不一样, 那么hash值一定不一样, hashCode和hash值是一一对应关系
如果一个key-value数据在数组x下标位置, 当这个数组扩容, 扩容完成之后, 这个key-value数据要重新散列, 要么还在x位置, 要么在旧数组长度+x位置
因为默认的扩容机制是2

二、数据结构

class HashMap{
   
    Node[] table; // HashMap的底层数组
    int size;
    float loadFactor; // 加载因子
    float DEFAULT_LOAD_FACTOR = 0.75f;	// 默认加载因子
    int threshold; // 阈值
    // ......
}

// 存储到HashMap底层Node数组中结点的类型:
// 添加key-value数据,实际上存储到对应经过计算 hash 值取模得到的下标的位置
class Node<K,V>  {
   
	final int hash;		// 扩容数组后,原本存在某个位置的元素可能改变位置
	final K key;
	V value;
	Node<K,V> next;    	// HashMap底层数组中是链表
}

三、resize() 扩容方法

给定数组长度点和 ArrayDeque 中的 initialCapacity 类似,只是 initialCapacity 是大于,没有等于
在构造方法里给 HashMap 一个长度, 它会把这个长度变成一个大于等于这个值的最小的2的幂值作为底层数组的长度
HashMap 的底层结构一直会保持2的幂值

final Node<K,V>[] resize() {
   
	
	// 旧数组
	Node<K,V>[] oldTab = table;
	
	// 旧数组长度
	int oldCap = (oldTab == null) ? 0 : oldTab.length;
	
	// 旧阈值
	int oldThr = threshold;
	
	// 新数组长度,新阙值
	int newCap, newThr = 0;
	
	if (oldCap > 0) {
    // 场景二:不是第一次添加数据
	    if (oldCap >= MAXIMUM_CAPACITY) {
   
	        threshold = Integer.MAX_VALUE;
	        return oldTab;
	    }
	    else if 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值