Hashmap 源碼分析
- put()null的處理,重複key的處理,hash衝突的鏈表處理
- indexfor() hash算法從而獲得槽點的巧妙用處
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
------int hash = hash(key);
| ----- int i = indexFor(hash, table.length);
|
/**
循環遍歷,找到合適位置插入,可以看到e ==null 跳出循環,存儲Entry<key,value>對象
e!=null hash衝突, e = e.next 這裏是解決hash衝突的關鍵,我們叫做拉鍊法,增加了一個表存儲Entry<key,value>對象
舉個例子大家就懂了:
第一個鍵值對A進來,通過計算其key的hash得到的index=0,記做:Entry[0] = A。
一會後又進來一個鍵值對B,通過計算其index也等於0,現在怎麼辦?
HashMap會這樣做:B.next = A,Entry[0] = B,如果又進來C,index也等於0,
那麼C.next = B,Entry[0] = C;這樣我們發現index=0的地方其實存取了A,B,C三個鍵值對,
他們通過next這個屬性鏈接在一起
*/
| for (Entry<K,V> e = table[i]; e != null; e = e.next) {
| Object k;
|
| if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
|
| V oldValue = e.value;
| e.value = value;
| e.recordAccess(this);
| return oldValue;
| }
| }
|
| modCount++;
| addEntry(hash, key, value, i);
| return null;
| }
|
|
|
|--➡️ final int hash(Object k) {
| int h = hashSeed;
| if (0 != h && k instanceof String) {
| return sun.misc.Hashing.stringHash32((String) k);
| }
|
| h ^= k.hashCode();
|
h ^= (h >>> 20) ^ (h >>> 12);
| return h ^ (h >>> 7) ^ (h >>> 4);
| }
|
|
|---➡️ static int indexFor(int h, int length) {
/**
& 與運算符(0 0 爲0 ,1 0 爲 0,1 1 爲1),
2的冪的二進制有基礎的同學可以發現它的二進制前幾位都是0,
這麼做的後果是hash值 h 與length進行 &運算始終是<= length-1 的,
最後返回key的槽點值
*/
return h & (length-1);
}