Map元素插入过程图:
/**
*
* onlyIfAbsent 默认为false,即在key值相同的时候,用新的value值替换原始值
*/
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
//获取key对应的hash值 ①
int hash = spread(key.hashCode());
//链表的长度,开始值为0;当hash值冲突时,key值不同,则会插入到链表中
int binCount = 0;
//在数组中插入node节点
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
//如果map中什么都没有,则为最开始put元素,此时去初始化table
if (tab == null || (n = tab.length) == 0)
//② 初始化table,之后再执行下一步插入node
tab = initTable();
//初始化table后,下一次循环到这里,第一次插入元素时,这个位置肯定是空的
//这里就是查找key对应数组位置是否有node,没有则创建node节点,放入数组中
//③ 获取对应位置的node
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
//对应位置为空,则创建节点并插入
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break; // no lock when adding to empty bin
}
//扩容时,会在数组最后添加forward节点,
//此时节点的hash值为-1,表示有线程在执行扩容操作
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
//对应的节点不为空,也不是扩容标识节点,则说明发生hash冲突
//若是链表,则通过链表方式添加
//若是红黑树,则通过红黑树方式添加
V oldVal = null;
//同步操作
synchronized (f) {
if (tabAt(tab, i) == f) {
//fh = f.hash
if (fh >= 0) {
//设置链表长度为1
binCount = 1;
//遍历位置的链表,若只有1个节点,则在节点后直接添加
//若链表中有相同的key值,则替换其value,这里应该与hashMap中一致的
for (Node<K,V> e = f;; ++binCount) {
K ek;
if (e.hash == hash &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
oldVal = e.val;
//默认替换,如果onlyIfAbsent为ture,则不替换旧值
if (!onlyIfAbsent)
e.val = value;
break;
}
Node<K,V> pred = e;
if ((e = e.next) == null) {
pred.next = new Node<K,V>(hash, key,
value, null);
break;
}
}
}
//如果对应的节点时一个红黑树结构,则使用红黑树的方式插入
else if (f instanceof TreeBin) {