//預設初始化長度
static final int default_initial_capacity = 16;
//最大長度
static final int maximum_capacity = 1 << 30;
//預設載入因子 size/capacity = loadfactor
static final float default_load_factor = 0.75f;
//放元素的陣列 要求長度是2的n次冪
transient entry table;
//已經放入的元素的個數
transient int size;
// capacity * load factor
int threshold;
//載入因子
final float loadfactor;
//map被改變的次數
transient int modcount;
構造器引數都是圍繞capacity(陣列長度)和loadfactor(載入因子)
public
hashmap
(int initialcapacity,
float loadfactor)
public
hashmap
(int initialcapacity)
public
hashmap()
public
hashmap
(map<
?extendsk,
?extends
v> m)
hash
這個方法是用來獲取加入進來的鍵值對 key的hash值
final
inthash
(object k)
// 預設為0
h = hashseed;
}//這三步獲取key的hash值
//為什麼要重新計算hashcode值,而不是將它直接去取模
// 因為length太小了,導致hashcode只有低位參與運算
// 而將它右移,讓高位參與到運算,減少碰撞概率
h ^= k.
hashcode()
; h ^=
(h >>>20)
^(h >>>12)
;return h ^
(h >>>7)
^(h >>>4)
;}
indexfor
用來獲取鍵值對 即將放入陣列的哪乙個下標
static
intindexfor
(int h,
int length)
get
public v get
(object key)
final entry
getentry
(object key)
return null;
}
put
public v put
(k key, v value)
}// 修改次數+1
modcount++
;// 如果之前map中沒有該key,新增乙個鍵值對
addentry
(hash, key, value, i)
;return null;
}void
addentry
(int hash, k key, v value,
int bucketindex)
// 建立新的鍵值對
createentry
(hash, key, value, bucketindex);}
void
createentry
(int hash, k key, v value,
int bucketindex)
resize
單獨看這個擴容方法,是因為多執行緒環境操作該方法,擴容後的陣列,可能會有乙個迴圈鍊錶
即 a ⇄ b -> null, 這樣的後果是遍歷該鍊錶時,會死迴圈
void
resize
(int newcapacity)
// 建立乙個新的陣列
entry[
] newtable =
newentry
[newcapacity]
;boolean oldalthashing = usealthashing;
usealthashing |= sun.misc.vm.
isbooted()
&&(newcapacity >= holder.alternative_hashing_threshold)
;// 在沒有特殊情況下,rehash還是false
boolean rehash = oldalthashing ^ usealthashing;
transfer
(newtable, rehash)
; table = newtable;
threshold =
(int
)math.
min(newcapacity * loadfactor, maximum_capacity +1)
;}void
transfer
(entry[
] newtable,
boolean rehash)
int i =
indexfor
(e.hash, newcapacity)
; e.next = newtable[i]
; newtable[i]
= e;
e = next;}}
} 假如有一條鍊錶是這樣的,裡面有2個節點物件
entry
a =
> entry
b a.key = a b.key = b
a.value = a b.value = b
a.next = b b.next = null
1.x執行緒執行到關鍵位置停止,切換到y執行緒。x中next = e.next = b。
2.y執行緒執行完transfer方法停止,切換回x執行緒,此時節點a,b物件已經發生變化
假如新的table中它們還是在乙個下標位置上,那麼此時
b.next = a , a.next = null; b =
> a =
> null
3.x執行緒繼續執行,第一次迴圈,此時x執行緒中的newtable中還沒有值
e.next = a.next = newtable[i]
= null
newtable[i]
= e = a (此時a是head 鍊錶是 a =
>null)
e = next = b
4.第二次迴圈
next = b.next = a (因為y執行緒,b物件發生變化,如果沒被修改應該是null)
e.next = b.next = newtable[i]
(head)
= a newtable[i]
= b (此時b變為head)
e = next = a
正常情況下 e = null, 這裡就結束了
5.第三次迴圈
next = a.next = null
e.next = a.next = newtable[i]
(head)
= b newtable[i]
= a e = next = null
結束 此時鍊錶變成 a ⇄ b
JDK1 7HashMap原始碼解析
hashmap已經看了很多篇文章了,今天還是自己解析一遍吧。我先大致介紹下hashmap的內部結構再跟著原始碼解讀一番 眾所周知hashmap的內部就是乙個雜湊表 什麼是雜湊表?如果我們利用陣列可隨機訪問的特性,將要存入的鍵通過一種雜湊演算法轉換成乙個數字,並把這個數字轉換成陣列的下標,然後將鍵和他...
JDK1 7 HashMap原始碼解析
在jdk1.7中,hashmap底層資料結構是由陣列和鍊錶構成,陣列存放entry物件,而entry物件是對鍊錶頭部第乙個元素的引用。arraylist中add方法是通過下標的累加進行資料的插入,hashmap則不同。hashmap通過獲取key的hashcode值,而hashmap中構成其陣列的預...
JDK1 7的HashMap原始碼解讀
default initial capacity 初始化容量,中為1 4 即為16。為什麼要這樣寫呢?maximum capacity 最大容量,中衛1 30 即為2的30次冪。30次冪的原因是 改屬性為int型別,int型別最大為4個位元組,共32個二進位制位,理論上可以向左移動31次,即31次冪...