put方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
putVal方法
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;//定义了一些辅组变量
if ((tab = table) == null || (n = tab.length) == 0)//将table赋值给tab,n记录tab的长度
n = (tab = resize()).length;//如果tab为null或者 tab的长度为0,则对tab初始化,resize代码解读在下面,n为数组的长度
if ((p = tab[i = (n - 1) & hash]) == null)//将i赋值为index,p赋值为 tab[i]的值,如果为null,说明这个位置没放过东西---------------1
tab[i] = newNode(hash, key, value, null);//直接将内容 new一个新的Node,然后放入到tab[i]中
else {//此时表示 tab[i]中已经放了内容-------------------------------------------------------------------------------------1
Node<K,V> e; K k;//定义辅助变量
if (p.hash == hash && //当前p指向的节点的hash是否等于传过来的hash
((k = p.key) == key || (key != null && key.equals(k))))//判断key的地址是否一样 再去判断 key的内容是否相等 这个判断顺序也是有讲究的,只有hash值相同,才有可能key相同, 如果key==p.key说明是同一个对象,后面的不需要执行,否则再去equals
e = p;//如果满足上面的条件,说明传过来的key已经存在与table数组中 将e指针指向p,也就是e指向了这个节点
else if (p instanceof TreeNode)//如果是红黑树,则进行红黑树的插入
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {//不是红黑树,链表 进行插入 节点的插入
for (int binCount = 0; ; ++binCount) {//此时p已经是第1个节点的----------------------------2
if ((e = p.next) == null) {//将e指向p的后面的节点,如果后面是null了,说明到了尾部了
p.next = newNode(hash, key, value, null);//直接插入
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 这里的-1就是因为p一开始就是第1个节点了,所以对于一开始而言,链表长度为8的时候,不会进行树化,只有第9个进来的时候才会进行树化
treeifyBin(tab, hash);//将链表进行树化的操作 注意,这里进去不会直接就树化,要判断是否满足if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) resize(); 如果tab为null或者tab的长度小于64,则resize
break;//跳出-----------跳出2
}
if (e.hash == hash &&//和上面的操作一样,符合了该条件的话,说明,在这个链表中存储key和要插入的节点的key一样的节点,并且此时e指向这个节点
((k = e.key) == key || (key != null && key.equals(k))))
break;//跳出-----------跳出2
p = e;//移动p指针,配合上面的 e = p.next 完成p的向后移动
}
}
if (e != null) { // 如果满足条件,说明链表中存在了同样的key
V oldValue = e.value;//记录老value
if (!onlyIfAbsent || oldValue == null)
e.value = value;//更新value
afterNodeAccess(e);//空方法,这个是留给linkedHashMap实现的
return oldValue;//返回老的value
}
}---------------------------------------------------------------------------------------------------------------------
++modCount;//修改的次数
if (++size > threshold)//如果size大于了阈值(这个阈值负责的是是否扩容 默认threshold = cap * 0.75)
resize();//扩容
afterNodeInsertion(evict);//空方法,留给其他继承者去实现的
return null;//返回null说明之前没有同样的key
}
resize()方法
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;//记录老的table
int oldCap = (oldTab == null) ? 0 : oldTab.length;//记录老的table的容量
int oldThr = threshold;// 记录老的树化的阈值(容量*负载因子)
int newCap, newThr = 0;//定义新的容量 新的树化的阈值
if (oldCap > 0) {//如果老的容量大于0,就不是进行初始化操作---------------------------------------------------1
if (oldCap >= MAXIMUM_CAPACITY) {//容量也不能无限大,当容量大于等于 最大的容量,则将树化的阈值赋值为最大容量
threshold = Integer.MAX_VALUE;
return oldTab;//直接返回,因为此时已经达到最大了,最多就是用完所有的容量,已经将树化的阈值调到最大了
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&//新的容量为老的容量的2倍,如果小于最大的容量大于默认容量
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // 将新的树化阈值调整为原来的2倍, 16(12)->32(24)->。。。。
}
else if (oldThr > 0) // 老的容量不大于0,但是老的树化阈值大于0的时候,将新的容量设置为老的树化的阈值----------------1
newCap = oldThr;
else { // 老的容量不大于0,老的阈值不大0的时候,进行初始化---------------------------------------1
newCap = DEFAULT_INITIAL_CAPACITY;//将新的容量设置为 16(默认的大小)
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//默认的树化的阈值 16 * 0.75
}
if (newThr == 0) {//如果新的树化阈值等于0,此时表示调用了HashMap的默认构造函数生成的对象,此时将树化阈值设置为ft和max的最小值
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;//将新的树化阈值赋值给 全局变量树化阈值
/* 接下来进行老的数据搬运进新的位置中去 */
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;//完成新数组的声明
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {//遍历
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;//将搬运的老位置清空为null
if (e.next == null)//这个桶中只有一个元素
newTab[e.hash & (newCap - 1)] = e;//直接将这个元素放入新桶的新的位置
else if (e instanceof TreeNode)//如果是红黑树,则进行树的插入,与链表不同
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order 不是红黑树,进行插入的步骤
Node<K,V> loHead = null, loTail = null;//记录 与oldCap为0的节点
Node<K,V> hiHead = null, hiTail = null;//记录 与oldCap不为0的节点
Node<K,V> next;
do {// do-while 进行链表的整体遍历移动到新数组中去
next = e.next;// next指向 e的后面一个
if ((e.hash & oldCap) == 0) {//将e链表中的元素分为 与oldCap 为0和不为0两种
if (loTail == null)//如果尾部为null说明 head肯定也是空
loHead = e;// 将 head赋值为e
else//否则,将尾部的下一个 赋值 为 e
loTail.next = e;
loTail = e;//将尾部后移动
}
else {//记录 与oldCap不为0的节点
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);//将e指针指向 e的后面一个
if (loTail != null) {//将hash低位的节点 还是放置的在新数组的老index下
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {//将hash高位的节点,放置在新书的oldCap+j的index下
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;//返回新tab
}
如有错误,欢迎指出。