hashmap基於map介面實現,元素以鍵值對的方式儲存,並且允許使用null 建和null 值, 因為key不允許重複,因此只能有乙個鍵為null,另外hashmap不能保證放入元素的順序,它是無序的,和放入的順序並不能相同。
hashmap的容量,預設是16
/**
* the default initial capacity - must be a power of two.
*/static final int default_initial_capacity=1
<<4;
// aka 16
hashmap的載入因子,預設是0.75
/**
/*** the load factor used when none specified in constructor.
*/static final float default_load_factor
=0.75f;
當hashmap中元素數超過容量*載入因子時,hashmap會進行擴容。
hashmap本質是乙個一定長度的陣列,陣列中存放的是鍊錶。
hashmap類中的元素是node類,翻譯過來就是節點,是定義在hashmap中的乙個內部類,實現了map.entry介面。
node類定義的原始碼:
static
class
node
v>
implements
map.entry
v>
public final k
getkey()
public final v
getvalue()
public final string tostring()
public final int hashcode()
public final v
setvalue
(v newvalue)
public final boolean equals
(object o)
return
false;}
}
node類的基本屬性有:
hash:key的雜湊值
key:節點的key,型別和定義hashmap時的key相同
value:節點的value,型別和定義hashmap時的value相同
所以node可以變成鍊錶,但不是雙向鍊錶,裡面有乙個hash的int值,這個值是現算得(高位取反,計算後去低位異或),它和key有關,但是不是key的hashcode值
由node節點組成鍊錶之後,hashmap定義了乙個node陣列:table
transient node table;
我們通過put方法來看往乙個空的hashmap裡放乙個值得時候會發生什麼。
1.當使用者呼叫put(k,v)方法,物件執行該方法。
我們進去put方法的原始碼
public
vput
(k key,
v value)
/** * 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
*/
2.物件執行putval方法final v
putval
(int hash,
k key,
v value, boolean onlyifabsent,
boolean evict)
threshold = newthr;
@suppresswarnings()
nodev>
newtab =
(nodev>
)new
node
[newcap]
;//建立乙個長度為16的陣列
table = newtab;
...return newtab;
table初始化完成後,putval依次放值
++modcount;if(
++size > threshold)
resize()
;afternodeinsertion
(evict)
;return
null
;
final v
putval
(int hash,
k key,
v value, boolean onlyifabsent,
boolean evict)
if(e.hash == hash &&
((k = e.key)
== key ||
(key !=
null
&& key.
equals
(k))))
break
; p = e;}}
if(e !=
null)}
++modcount;if(
++size > threshold)
//注釋5
resize()
;afternodeinsertion
(evict)
;return
null;}
}}
**解析:
1,注釋1,table對應位置無節點,則建立新的node節點放入對應位置。
2,注釋2,table對應位置有節點,如果hash值匹配,則替換。
3,注釋3,table對應位置有節點,如果table對應位置已經是乙個treenode,不再是node,也就說,table對應位置是treenode,表示已經從鍊錶轉換成了紅黑樹,則執行插入紅黑樹節點的邏輯。
4,注釋4,table對應位置有節點,且節點是node(鍊錶狀態,不是紅黑樹),鍊錶中節點數量大於treeify_threshold,則考慮變為紅黑樹。實際上不一定真的立刻就變,table短的時候擴容一下也能解決問題,後面的**會提到。
5,注釋5,hashmap中節點個數大於threshold,會進行擴容
第二次擴容
final nodev>
resize()
else
}while
((e = next)
!=null);
if(lotail !=
null)if
(hitail !=
null)}
}}}return newtab;
第二次擴容完成
首先建立hashmap的時候,底層陣列還沒有初始化,它會去檢查當前的table,如果table為空,會去拿到預設的大小(16)和載入因子(12),建立長度為16的陣列。以node為節點,put方法的key轉為hash(高反低異),節點的hash值和當前長度-1進行與運算,也就是對16取模,得到當前值應該被防止的位置。如果這個位置已經由值存在,則被放置到已經存在的值之後(鍊錶)。
當一直存放到第13個值得時候(第12個值不擴容),呼叫resize方法 長度擴容到32,載入因子為24 。
原來的值可能形成鍊錶,鍊錶之前的值是對16取模,比如在3這個節點上,現在要對32取模,那麼就有可能在兩個節點上,要麼在3上,要麼在19上 所以求需要將鍊錶上的值都拿出來,來確定是在3這個節點上還是19節點上,判斷完成後,有乙個lohead(低位)和hihead(高位),兩個都形成了乙個新的鍊錶,於是將lohead的值儲存到原來的位置上, hihead的值將被放到19的位置上
hashmap底層實現原理
每次初始化hashmap都會構造乙個table陣列,而table陣列的元素為entry節點。static class entryimplements map.entryhashmap也可以說是乙個陣列鍊錶,hashmap裡面有乙個非常重要的內部靜態類 entry,這個entry非常重要,它裡面包含了...
HashMap底層實現原理
hashmap map new hashmap 在例項化以後,底層建立了長度為16的一維陣列entry table 已經執行過put操作.map.put key1 value1 呼叫key1所在類的hashcode 計算key1雜湊值,此雜湊值經過某種演算法計算後,得到在entry陣列中的存放位置 ...
HashMap底層實現原理
一 jdk1.7中hashmap的底層實現原理 首先,當我們通過hashmap的構造方法建立乙個hashmap物件時,底層就會建立乙個entry型別的一維陣列 預設初始化長度為16 當我們執行put操作的時候,會呼叫key所屬類的hashcode方法計算出key的hash值,然後將hash值通過雜湊...