注释HashMap 存/取值
put方法
/**
* 将指定值与该映射中的指定键相关联。
* 如果map中key的值已经存在,则新的值会取代旧的值
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/**
* 实现Map的put方法以及相关方法
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, 不存在key对应的value
* @param evict if false, 表处于创建模式.
* @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;
/**
注释:先进性赋值 tab = table ; n= tab.length;
如果表为table=null 或者table的长度为0,则调用 resize());方法将tab初始化,n为新创建table的长度16
*/
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
/**
i = (n - 1) & hash是根据key值经过hash算法后获取所在数组的索引。
tab[i = (n - 1) & hash]);获取数组索引出的链表( Node<K,V>);并且把p = 这个链表
if 链表为null,就再索引处新建一个链表结构
*/
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
//下面是 索引处的存在链表
else {
Node<K,V> e; K k;
/**
if else if else 是为了给e赋值。由于当链表长度超过8的时候,就会变为红黑树结构。
*/
//如果存在和key相等的node,则将node取出,以便下面替换已经存在的值
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//如果索引为红黑树结构,则将值放入平衡树中,并返回这个TreeNode 赋值给e,以便后面操作
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else { //非红黑树结构
//既然没有key的node,找到最后一个node,并且把这个新的key-value生成一个node,
//并把这个新node放到这个node的next下
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//如果链表长度大于8 就转换为红黑树结构
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))))
break;
p = e;
}
}
//获取新建节点的值,赋予put方法的value,并返回旧的value
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold) //当梳理大于threshold时候,会进行扩容。
resize();
afterNodeInsertion(evict);
return null;
}
//modCount和size都表示key-value个数的总和。
//threshold = capacity*loadFactor;
//capacity就是指HashMap中桶的数量。默认值为16
//loadFactor译为装载因子。装载因子用来衡量HashMap满的程度。loadFactor的默认值为0.75f。
//计算HashMap的实时装载因子的方法为:size/capacity,而不是占用桶的数量去除以capacity。
下面是图解,以便帮助读者更加直观的理解。
put方法中用到的方法
resize(); //重新计算table长度
newNode(hash, key, value, null); //table索引出没有链表,新建链表
TreeNode<K,V> extends LinkedHashMap.Entry<K,V> //红黑树结构对象
((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);//将数据放入红黑树中
treeifyBin(tab, hash); //将链表结构转化为红黑树结构
afterNodeAccess(e); //没有任何操作
afterNodeInsertion(evict);//没有任何操
get方法
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
/**
* Implements Map.get and related methods
*
* @param hash hash for key
* @param key the key
* @return the node, or null if none
*/
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) { //如果hash所在索引的table存在数据
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k)))) //如果第一个节点为所找的数据
return first;
if ((e = first.next) != null) { //第一个节点不是想要的数据。
if (first instanceof TreeNode) //如果此处为红黑树结构,就按照树查找节点
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {//循环遍历列表,知道找到相应的节点。
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
get中用到的方法
((TreeNode<K,V>)first).getTreeNode(hash, key);
遗留问题:
hashMap中阈值设计理论,扩容运算,红黑树数据结构。