HashMap put方法分析
public V put(K key, V value) {
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);
}
如果key为空的话,直接返回0(存放位置的index也为0。因为Node.hash为0,通过路由寻址公式:(tabel.length - 1) & node.hash,无论与什么数相与,最后的结果肯定都为0,相反,如果在index = 0的位置上存在一个Node,那么该Node.hash一定是0);若不是,先调用key的hashCode方法算出key的哈希值。^(位异或运算,相异为1,相同为0)。h向右无符号右移16位,前边补零。之后再和原来的哈希值进行位异或运算。目的就是为了让高位也参与路由运算。(路由寻址公式:(tabel.length - 1) & node.hash)
为什么要用异或运算?

根据图中可知,异或运算中的0和1比例是1:1出现的。所以用位异或运算更可以让数据更加随机,更加散列。
HashMap中put方法详解:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
//tab:引用当前hashMap的散列表
//p:表示当前散列表的元素
//n:表示散列表数组的长度
//i:表示路由寻址的结果
HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, i;
//延迟初始化,第一次调用putVal()时会初始化hashMap对象中的最耗费内存的散列表
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//对应的写法:HashMap h ;HashMap h = new HashMap();(只创建了没有放东西)
//如果tab不为空的话,n就一定会被赋值。反过来说:n不被赋值只有一种情况:tab为null;
//最简单的一种情况:寻址找到的桶位,正好是null。这个时候直接添加就好。
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
//不是最简单的情况。就说明,已经存在一个或多个元素(链表or红黑树)
else {
HashMap.Node<K,V> e; K k;
//e:Node临时元素
//寻址找到的元素p的哈希值和要添加的hash值相同 并且 (p的key与要添加的key相等,都为null 或者
//(key不为空并且key和元素p的key相同)
//如果条件满足,则e就会引用寻址找到的元素p。表示后续要进行替换操作。
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//如果寻址找到的元素p已经树化了
else if (p instanceof TreeNode)
e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
//如果寻址找到的元素p已经成为链表
else {
//链表的情况,而且俩表的头元素和我们要输入的key不一致。
for (int binCount = 0; ; ++binCount) {
//迭代到最后一个元素了,也没有找到一个和要输入的key相同的元素
//说明需要加入到当前链表的末尾。
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//当前链表的长度达到了树化阈值,需要进行树化
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//条件成立的话,说明找到了相同key的元素,需要进行替换操作。
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
//寻址找到的元素p的key为null或者与要添加的key相同,要替换value
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
//modCount:表示散列表被修改的次数,替换Node元素的value的不计数
++modCount;
//size:散列表中Node的数量。
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
本文详细分析了HashMap的put方法,从hash()函数开始,解释了哈希值的计算过程,探讨了位异或运算在散列分布中的作用。接着,文章详述了putVal()方法内部的逻辑,包括链表和红黑树的插入操作,以及何时进行扩容和树化。通过对源码的解读,揭示了HashMap在处理冲突和保证性能方面的策略。

1105

被折叠的 条评论
为什么被折叠?



