HashMap存储数据时,调用put方法,源码及分析如下所示:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/**
* Implements Map.put and related methods.
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
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)
// 1、首次插入数据时,初始化数组
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
// 2、当对应桶中无数据时,将元素存入数组中
tab[i] = newNode(hash, key, value, null);
else {
// 3、对应桶中有数据
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
// 3.1 数组中元素的key与传入的key相同时,返回对应的节点
e = p;
else if (p instanceof TreeNode)
// 3.2 当数据中的节点为树节点时,将数据存入红黑树中,并返回对应节点
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
// 3.3 数组中元素的key与传入的key不同,且不为树节点时,遍历该桶对应的链表
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
// 链表遍历完了仍未找到key相同的节点时,将数据添加到链表尾
p.next = newNode(hash, key, value, null);
// 如果链表长度大于等于树化阈值(8)时,进行树化操作
// (1)当数组长度小于最小树化容量阈值(64)时,进行数组扩容操作
// (2)当数组长度大于等于最小树化容量阈值时,将链表转为红黑树
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))))
// 链表中元素的key与传入的key相同时,返回
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
// 4、将节点中的value 设为新值
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
// 5、当集合元素数量大于阈值时,进行集合扩容操作
resize();
afterNodeInsertion(evict);
return null;
}
问题:
1、HashMap什么时候初始化数组?
答:首次插入数据时,调用resize()方法初始化数组
2、数据插入到链表时,会将其插入到链表的什么位置?
答:插入到链表尾
3、插入数据时,什么时候会将链表转为红黑树?
答:当链表长度大于等于树化阈值(TREEIFY_THRESHOLD=8),且数组长度大于等于最小树化容量阈值(MIN_TREEIFY_CAPACITY=64)时,将链表转为红黑树