jdk8源码
public HashMap()
public HashMap(int initialCapacity)
public HashMap(int initialCapacity, float loadFactor)
public HashMap(Map<? extends K, ? extends V> m)
哈希表,就是一个Node数组,而Node是HashMap里一个静态内部类,实现了Map接口里的Entry接口,可以看出Node类其实是一个链表节点,所以HashMap就是使用了链地址法来解决冲突的。
Node类重写了hashCode方法,把key和value的哈希值异或起来。
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
put方法内部是调用了putVal方法,putVal(hash(key), key, value, false, true)
。
多了三个参数,第一个是哈希值,后两个是onlyIfAbsent和evict。
onlyIfAbsent是put方法用来判断是否覆盖。
evict应该是LinkedHashMap用到的,默认都为true。
(n - 1) & hash
,因为n=2^k,所以这个操作就是取哈希值的低k位,其实就是取模。注意这里的相等比较用到了==和equals(),这是key覆盖的情况。
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
如果哈希表上不是链表节点,而是平衡树节点,调用putTreeVal方法。
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
这是哈希位置相同而key不同的清空,也就是哈希冲突。
遍历对应位置的链表,加到表尾,如果链表中间有相等的key,也是覆盖。
当同一位置链表节点数大于等于8个时,调用treeifyBin方法进行树化,将链表转化为红黑树。
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
接下来是处理key覆盖的清空,会返回旧的value。
put方法的onlyIfAbsent是false,所以无论如何都会覆盖相同key的值。
putIfAbsent方法的onlyIfAbsent是true,所以只有当oldValue是null的时候,才会覆盖。
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
Test code1
import java.util.HashMap;
public class LearnHashMap {
public static void main(String[] args) {
HashMap<String,String> a=new HashMap<>();
a.put("abc","FF");
a.putIfAbsent("abc","FFF"); //不会覆盖,仍然是{"abc":"FF"}
System.out.println(a.put("abc","kk"));
}
}
最后是处理对应哈希表下标没有元素的清空,size要加1,如果大于threshold,就扩容。
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
这个方法可以返回最近且大于cap的2的次幂,可以用来根据容量capacity来确定阈值threshold。
源码就不看了,神仙位运算看不懂。
每次都是2倍的扩容,MAXIMUM_CAPACITY就是1<<30。
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
如果容量为0,而阈值大于0,直接让容量为阈值。
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
都为0,则用默认值,默认阈值等于默认容量默认加载因子,即160.75=12。
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
然后就是new一个新的节点数组,大小是原数组的两倍,然后将原数组的链表和红黑树copy过去。
这一步可以把原本一个节点的链表分成两个。
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
将链表转红黑树
原文:https://www.cnblogs.com/zxcoder/p/12252830.html