1java
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
static final int hash(Object key) { int h; //k求的hashCode後, 高16位和低16位作異或運算(同則0,異則1,11->0,00->0, 10->1) //爲何呢?減小碰撞. //爲何能減小碰撞? /* if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null) */ // n 是2的冪次方, 所以n-1的二進制,除了符號位是0,其餘都是1; 由於作&運算後結果的離散性,取決於hash值的離散性 // 生活中都有規律,使用的key要麼通常偏小(整型,可能是short),要麼偏大(長字符串的hash). 高低8位作異或,打破規律.(本身胡謅的.......) return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); //無符號右移16位,高位補0 // 111111111 11111111 11111111 11111111 ^ 00000000 00000000 00000000 01111001 // }
/* 1 首次插入,要初始化table,分配空間 2 是否有碰撞: a 無,則插入 b 有,則須要遍歷鏈表(或紅黑樹)查找是否存在相同的key [ 碰撞耗費性能 ] ba: 存在相同的key-> 替換 bb: 不存在相同的key,插入尾部 */ 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) n = (tab = resize()).length;// 1 HashMap是lazy的.第一次put時,才調用resize,初始化table,分配空間 // 若是未發生碰撞 if ((p = tab[i = (n - 1) & hash]) == null) //用&運算來求餘,提升效率 tab[i] = newNode(hash, key, value, null); else { //若是發生碰撞 Node<K,V> e; K k; if (p.hash == hash && // 若是key的hash值相同,且equals(或同一內存地址->同一對象) ((k = p.key) == key || (key != null && key.equals(k)))) e = p; //對value進行替換 // 若是是紅黑樹節點 else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { //若是是鏈表,遍歷鏈表 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) {//到了鏈表末尾 p.next = newNode(hash, key, value, null);//鏈表末尾新增一個節點 if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash);// 鏈表節點數超過閾值,將其轉換成爲紅黑樹 break; } // 遍歷鏈表,查找是否有key相同的元素 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; //若是有,則break, 此時e爲key相同的節點 p = e; } } if (e != null) { // existing mapping for key V oldValue = e.value; // 對value作替換 if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; //返回舊的value } } ++modCount;//對修改次數加1 if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }