HashMap 內(nèi)的主要數(shù)據(jù)結(jié)構(gòu)
- 內(nèi)部類 Node<K,V>(實(shí)現(xiàn)了Map.entry接口货邓,存儲(chǔ)key-value的基礎(chǔ)類笙隙,鏈表)
- table (Node<K,V> 數(shù)組)
基本思路是(后續(xù)會(huì)做更詳細(xì)的解讀):
- 根據(jù)key的hash值確定table的index索引
- table的每個(gè)index實(shí)際上存儲(chǔ)的是Node鏈表笋额,由第1步確定索引后齐佳,再循環(huán)鏈表如果存在相同key(key.equals)則替換Node的value值增热,否則鏈表尾添加新Node
構(gòu)造函數(shù)
無(wú)參構(gòu)造函數(shù)
threshold 變量為閾值顷级,table大小查過(guò)該變量就會(huì)觸發(fā)擴(kuò)容(resize)
而 threshold = capacity * loadFactor
public HashMap() {
// 默認(rèn)擴(kuò)容因子0.75
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
// table 的初始化推遲到了 map.put(key,value) 的resize()時(shí)
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
/** 省略部分代碼 **/
// 初始化table
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
Map參數(shù)構(gòu)造函數(shù)
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;// 默認(rèn)擴(kuò)容因子0.75
//主要做兩件事情
//1. 利用上一篇文章提到的 求2的冪 的方法,確定HashMap的size
//2. 將 m 內(nèi)的key-value(即node)逐個(gè)復(fù)制到該HashMap中
putMapEntries(m, false);
}
初始變量構(gòu)造函數(shù)
public HashMap(int initialCapacity, float loadFactor) {
// 參數(shù)的合法性校驗(yàn)
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
// threshold 為擴(kuò)容閾值错森,超過(guò)該值即觸發(fā)resize
// tableSizeFor即上篇文章介紹的求2的冪方法
this.threshold = tableSizeFor(initialCapacity);
}
// table 的初始化也推遲到了 map.put(key,value) 的resize()時(shí)吟宦,與無(wú)參構(gòu)造函數(shù)不同的是,此處初始化的大小涩维,正是你設(shè)置的initialCapacity殃姓,而threshold也被重新計(jì)算
// 利用threshold做一個(gè)存儲(chǔ)的過(guò)渡,完美做到了一點(diǎn)都不浪費(fèi)不啰嗦的優(yōu)良傳統(tǒng)
newCap = oldThr;
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
// 阿里開(kāi)發(fā)規(guī)約建議使用該 初始化函數(shù)瓦阐,設(shè)置初始容量蜗侈,盡可能的避免map.resize帶來(lái)的性能消耗,也提高存儲(chǔ)空間的利用率(用多少聲明多少)
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
小結(jié)
綜上可以發(fā)現(xiàn)睡蟋,初始化主要是設(shè)置table容量相關(guān)值踏幻,具體的table大小初始化推遲到了putValue階段,這也就是下篇文章要介紹的重點(diǎn)戳杀。