HashMap是一种使用较多的数据结构,要使用好HashMap的话还是有必要对HashMap内部实现机制多做一些了解的.这样我们才可以灵活运用必要的时候可以对功能做一些改变或者加强来达到我们自己的需求.下面简单地对HashMap的存放元素的过程做了一些分析.
分析中对有些组合成一步的步骤拆分出来来写了,主要是一些条件的判断步骤包含了赋值的操作,所以提出来可以方便看清楚,不过源代码这种写法还是值得借鉴的.
public V put(K key, V value) {
// hash相同,值不一定相同,hash不同,值一定不同
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) {
// HashMap的数组就是由Node数组组成的,Node是链表的一个节点
Node<K,V>[] tab;
// 数组的中的某一个元素
Node<K,V> p;
int n, i;
tab = table;
n = table.length;
// 空map的情况
if (tab == null || n == 0)
n = (tab = resize()).length;
// 位与运算:任何数与1进行位&都是原数,任何数与0位与都是0;
// hash/2^n取余计算位置,hash/2^n取余相当于只要hash的低n位,取低n位的方法就是&(2^n - 1)
i = (n - 1) & hash;
p = tab[i];
if (p == null) // 数组该位置没有元素,直接存储到该位置,Node就是链表中的元素
tab[i] = newNode(hash, key, value, null);
else { // 数组该位置有元素,就是产生了hash冲突
Node<K,V> e; K k;
// 原位置上存放的元素的key
k = p.key;
/*
HashMap是由数组 + 链表 + 红黑树组成的
这里可以这样理解一下,Map是由Node数组组成的,实际上数组中的每一个元素都是存放的是某一个
具体的Node而不是一个链表,通过这个Node可以拿到链表的下一个元素,依次遍历下去.
所以这一步判断是对链表的第一个元素的判断
*/
// p.hash就是原来存放的元素(链表)的第一个元素的hash
if (p.hash == hash &&
(k == key || (key != null && key.equals(k)))) // existing mapping for key链表的第一个元素的键和要存储元素的键相同
e = p;
else if (p instanceof TreeNode) // 该位置是一个红黑树
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else { // 遍历链表中的每一个元素看是否有键值冲突的情况
for (int binCount = 0; ; ++binCount) { // 结束的条件为p.next = null;
e = p.next;
if (e == null) { // 链表下一个元素为空,证明没有键冲突的情况
p.next = newNode(hash, key, value, null); // 在该链表后面追加元素
if (binCount >= TREEIFY_THRESHOLD - 1) // 满足树形化的条件,链表转为红黑树
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) // existing mapping for key发生了键冲突的情况
break;
p = e;
}
}
if (e != null) { // existing mapping for key
// 原来的位置存放的旧值
V oldValue = e.value;
// onlyIfAbsent = false;
if (!onlyIfAbsent || oldValue == null)
// 使用新的值代替原来的值,添加重复元素采取覆盖的策略
e.value = value;
afterNodeAccess(e);
// 如果发生hash冲突返回原来的存放的元素的value
return oldValue;
}
}
// 快速失败,如果只是覆盖旧值并不会进行下面的这些步骤
++modCount;
// 达到扩容的条件 *0.75
if (++size > threshold)
resize();
// evict = true; 空实现
afterNodeInsertion(evict);
// 添加新的元素之后返回的是null
return null;
}