HashMap source code analysis

This friend from Shanghai 2022-08-06 08:52:55 阅读数:151

hashmapsourcecodeanalysis

HashMap 源码分析 步步分析

1,首先看到put方法

 public V put(K key, V value) {

// 在其中调用了hash方法
return putVal(hash(key), key, value, false, true);
}
/** * 其当前方法是为了减少hash碰撞,降低hash冲突的几率 */
static final int hash(Object key) {

int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

2,往里追,putVal方法

 /** * putVal 则是当前hashMap核心方法之一 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {

Node<K,V>[] tab; Node<K,V> p; int n, i;
// 进入第一层判断,判断是否表是否为null,
// 则会去调用resize()方法,完成对table的初始化
// 其在resize里面做的一个事情,加载因子*当前数组的容量,
// 默认16,所以就是 0.75 * 16 = 12,得出一个临界值
// 则临界值,是当我们容量到达临界值时,完成的一个扩容的过程
if ((tab = table) == null || (n = tab.length) == 0)
// 初始化,感兴趣的小伙伴,可以往resize()里面追
n = (tab = resize()).length;
// 通过hash过后得到一个索引位置,判断当前位置是否有值,
// 如果没有值,则创建一个新的节点
if ((p = tab[i = (n - 1) & hash]) == null) // 确定当前位置没有值
// 首次加入话,必定会加进去的
tab[i] = newNode(hash, key, value, null);
else {
// 当前位置有值
// 如果当前值,则会进行多次判断
Node<K,V> e; K k;
// 首先第一次判断,判断当前索引的第一个位置的hash值是否跟
// 我正在添加进去的hash值是否相同,如果相同则 p 赋值给 e 
// 之后就会完成值的替换
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode) // 判断当前是否为树型
// 如果为树形,则会已树形的方式添加
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {

/** * 如果以上两个条件都不满足,则说明当前是一个链表形式的 * 所以我们可以看到,在其for循环,主要做的事情,循环对比 * 链表上的每一个节点,如果有一个节点相同,则break; */
// for 死循环
for (int binCount = 0; ; ++binCount) {

// p.next赋值给e 判断是否为null, 也就是说判断下个节点
// 是否有值
if ((e = p.next) == null) {

// 如果确实等于null 则在当前p的下个节点创建
// 一个新的节点
p.next = newNode(hash, key, value, null);
// 判断链表长度>=8 table长度>=64 转换为红黑树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
//会去调用treeifyBin方法,则不会马上进行
//树化,在treeifyBin中判断了table.length
//如果满足两个条件才会进行红黑树化
treeifyBin(tab, hash);
break;
}
/** * 如果上面p.next是有值的,则不会进入上面那个判断,则会进入当前 * 这个判断,其实这个判断跟刚刚开始进putVal方法那个判断大同小异 * 如果在当前循环比较过程中,发现有相同的,就break * 则后面 就会进到if (e != null)这个判断里面去 * 如果没有相同,则把当前这个e赋值这个p, * 在下次循环的开始if ((e = p.next) == null),由此可见 * p.next 又赋值了e 一节一节往后走,真是一个巧妙的设计 */
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
// 其当前判断,也就是我们上面所说的,如果满足当前条件
// 则意味着添加失败,因为它们在当前找到了那个值,并赋值给了e 
if (e != null) {
 // existing mapping for key
// 原来的旧值提取出来
V oldValue = e.value;
//onlyIfAbsent 传进来就是false,取反则为true
if (!onlyIfAbsent || oldValue == null)
// e.value = value; 这意味着当前当前新的value给
// 到了e.value ,从而发生了替换
// 这就是为什么HaspMap value 可以重复,如果key重复
// 则key的value会被替换
e.value = value;
afterNodeAccess(e); // 空实现方法,交给子类去实现的
//如果返回有值,则是添加失败,返回null,则添加成功
return oldValue; // 添加失败
}
}
++modCount;// 修改次数加加
// 当前table容量值 是否大于 临界值
if (++size > threshold)
resize(); // 如果满足 开始扩容
afterNodeInsertion(evict); // 空实现方法,交给子类去实现的
return null;// 添加成功
}
版权声明:本文为[This friend from Shanghai]所创,转载请带上原文链接,感谢。 https://javamana.com/2022/218/202208060851128015.html