static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 创建数组时默认的长度
static final float DEFAULT_LOAD_FACTOR = 0.75f; //载荷因子,如果数组存储占75%时,就要扩充为原来的二倍
static final int TREEIFY_THRESHOLD = 8; //表示当链表元素大于等于8时,就要触发转红黑树的判断机制,如果数组长度大于等于64时,链表才会转为红黑树,要不然就会扩容数组
static final int UNTREEIFY_THRESHOLD = 6; //当链表的元素个数小于等于6个时就会从红黑树转为链表
static final int MIN_TREEIFY_CAPACITY = 64; //表示如果要从链表转为红黑树,数组的长度最最小为64
2.putVal源码分析:
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { //hash是用hash(key)算出的key的hash值
Node<K,V>[] tab; Node<K,V> p; int n, i; //tab就表示结点型数组 p表示一个结点
if ((tab = table) == null || (n = tab.length) == 0) //创建数组
n = (tab = resize()).length; //n表示数组的长度
if ((p = tab[i = (n - 1) & hash]) == null) //i = (n - 1) & hash就相当于hash%n 首先2进制位运算肯定比取余运算快,其次不管是HashSet,LinkHashSet维护的table的容量都是2^n方,这也保证了(n-1)& hash 设计的合理性
tab[i] = newNode(hash, key, value, null); //如果此位置上为空,则将K,V封装成结点插入即可
else {
Node<K,V> e; K k; //此时p已经指向了数组对应位置的头节点,定义了一个结点e,键 k
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k)))) //如果头结点的键与要插入的键的相等,则证明只要更换值就行
e = p; //用e指向此结点,做个标记,直接跳出else{}语句
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); //看一下类型是不是红黑树,如果是则调用添加树节点的方法
else {
for (int binCount = 0; ; ++binCount) { //开始遍历链表,要找到插入的位置,注意此时bincount起始为0
if ((e = p.next) == null) { //e指向p的下一个结点,如果只有一个头结点,则e为null,那么直接在p.next处插入即可
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st //如果插入之后,bincount>=7,则证明有8个结点了,则调用触发是否转红黑树的判断代码
treeifyBin(tab, hash);
break; //直接中断
}
if (e.hash == hash &&
((k = e.key) == ey || (key != null && key.equals(k)))) //还未遍历完时,如果e指向的结点的键与要插入的键一样时,则证明只需要改值就行,e标记了一下
break; //直接中断
p = e; //如果上面两个if都不满足,则让p指向e,下一次循环e又指向p->next,这样就会达到遍历的效果。
}
}
if (e != null) { // existing mapping for key //判断e是否为null,则看一下是否只需要修改e所指结点的键值
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value; //将e所指结点的键值直接更改为value即可
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}