这一节具体分析下put的主要源码,下面先贴上源码再一步一步分析。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int number, index;
if ((tab = table) == null || (number = tab.length) == 0)
number = (tab = resize()).length;
if ((p = tab[index = (number - 1) & hash]) == null)
tab[index] = newNode(hash, key, value, null);
else {
Node<K,V> elememt; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
elememt = p;
else if (p instanceof TreeNode)
elememt = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((elememt = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (elememt.hash == hash &&
((k = elememt.key) == key || (key != null && key.equals(k))))
break;
p = elememt;
}
}
if (elememt != null) { // existing mapping for key
V oldValue = elememt.value;
if (!onlyIfAbsent || oldValue == null)
elememt.value = value;
afterNodeAccess(elememt);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
put的具体细节
put简单说就是这个值如果不在里面就直接放进去,如果已经有了就替换,如果满了就扩容后再放进去。
- 首先第一个if作用是为了判空,如果为true就去调用resize这个方法,这个方法不光是可以用来扩容,也做初始化的操作,如果时null或空的时候会根据设置的初始化容量来设置初始化。
- 第二个if先给index赋值然后判断tab数组在这个位置上是不是null,true就说明这个位置还没被使用就直接给这个位置通过newnode方法赋值
- else中的第一个if目的是为了判断要存放的map和当前的map是不是同一个,通过对比hash值和key,都相同就证明这两个是同一个,那就直接把这个p赋值给element
- 第二个if判断当前节点是不是树节点,如果是就用树的增加方式来增加并把返回的treenode给element
- 最后else是一个无限for循环
- 首先判断element.next指向的是不是null,true就直接newnode然后让p.next指向这个新建的node,这一步用的是尾插法,不断往链表后面去插入新的节点(java8之前用的是头插法和这个有点区别),紧接着判断当前记录的bincount数是不是已经超过了树的阈值,超过了就转化成红黑树(在java8之后才会转化成红黑树),然后跳出循环
- 如果p.next不是null而是已经有值那就判断和要存放的是不是相同的node,判断hash和key,相同就直接跳出循环。
- 如果以上都不是那就把element给p
- 如果element不是null,判断element.value是null或者onlyIfAbsent(允许改变已经存在的值),就把element的value改成要保存的value。afterNodeAccess这个方法是给linkedhashmap使用的
- 最后就是增加modecount(修改次数),并且判断当前size是不是已经超过了阈值,如果超过了就调用resize来扩容。