首先,hashmap儲存結構類似於位桶,總體結構是
位桶+鍊錶+紅黑樹,
這與之前版本的實現有所改進。
常量域
預設容量16
static final int default_initial_capacity = 1 << 4;
最大容量2的30次方
static final int maximum_capacity = 1 << 30;
預設載入因子
static final float default_load_factor = 0.75f;
這是鍊錶轉換為紅黑樹的門檻值,即對於乙個桶中的節點大於8時,就改為紅黑樹儲存
static final int treeify_threshold = 8;
和上面是逆過程,紅黑樹中節點少於6時,改為鍊錶
static final int untreeify_threshold = 6;
table中最少要有64個節點才會轉換為紅黑樹
static final int min_treeify_capacity = 64;
hashmap的成員變數
node table;//儲存key-value健值對的陣列,在第一次插入時初始化
set> entryset
int size;//hashmap中key-value健值對的數量
int modcount;//修改次數,在有效的put和remove操作中會自增1,主要用在迭代器中
final nodenextnode() while (index < t.length && (next = t[index++]) == null);
}return e;
}即迭代過程中,如果執行了put或remove操作,導致modcount與開始迭代前expectedmodcount的值不一樣,會丟擲異常
int threshold;//門檻值,大於這個值時將會進行resize操作
float loadfactor//載入因子,預設0.75
1.構造方法
hashmap共有4個構造方法
public hashmap()
public hashmap(int initialcapacity)
public hashmap(int initialcapacity, float loadfactor)
public hashmap(map<? extends k, ? extends v> m)
這裡說下第三個
public hashmap(int initialcapacity, float loadfactor)
中的tablesizefor方法
static final int tablesizefor(int cap)
對於引數cap,它將返回比cap大且最小的2的整數次方對,例如10,它返回16。
演算法原理即是每次向右移動2的0次方、1次方。。。然後與n相或,這系列計算後會得到從n的二進位制數從左到右不為0的位起,將後面的位全部變為1,即n=10 =00001010,會變為00001111=15,在返回時+1變為16
2.put的方法
public v put(k key, v value)
轉到 putval方法,它是final的
//引數意義分別為
//hash key的hash值
//key 健值
//onlyifabsent 如果為true不改變已經存在的舊值
//evict
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;}}
//e != null說明插入的值已存在
v oldvalue = e.value;
if (!onlyifabsent || oldvalue == null)
e.value = value;
//這方法是為 linkedhashmap準備的
afternodeaccess(e);
return oldvalue;}}
//修改次數+1
++modcount;
//size>門檻值了,重新計算容量
if (++size > threshold)
resize();
afternodeinsertion(evict);
return null;
}
再來看看treeifybin方法
final void treeifybin(node tab, int hash)
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
hd.treeify(tab);
}}
這裡面用到了min_treeify_capacity常量,也就是說如果健值對個數少於64它依然不會轉化為紅黑樹,它會呼叫了resize方法就行擴容,擴容後健值對會分散,即達到乙個鍊錶中的健值對個數不大於8.
再看resize方法
這之前我們看看什麼情況會進行reszie操作,即擴容操作
(1)前面所講的紅黑樹轉換時,擴容目的是為分散健值對,如何分散請看下面
(2)首次進行put的操作時,會進行初始操作,各個成員變數會賦預設值
(3)put方法中,當此時size>threshold(容量上限)時
final node resize()
//門檻值變為舊的兩倍
else if ((newcap = oldcap << 1) < maximum_capacity &&
oldcap >= default_initial_capacity)
newthr = oldthr << 1; // double threshold
}else if (oldthr > 0) // initial capacity was placed in threshold
newcap = oldthr;
//初始化
else
if (newthr == 0)
threshold = newthr;
@suppresswarnings()
node newtab = (node)new node[newcap];
table = newtab;
if (oldtab != null)
//位置為原來index+oldcap
else
} while ((e = next) != null);
if (lotail != null)
if (hitail != null) }}
}}
return newtab;
}
分析HashMap 的 JDK 原始碼
緣由 今天好友拿著下面的 問我為什麼 map.entry 這個介面沒有實現 getkey 和 getvalue 方法,卻可以使用,由此,開啟了一番查閱 jdk 原始碼的旅途 map map new hashmap map.put 1,張三 map.put 2,李四 map.put 3,王五 map....
hashmap原始碼分析jdk8
最近看了下jdk8的hashmap原始碼,相比於7,在儲存結構上有了些改變。1.在jdk8之前,hashmap的儲存結構是陣列 鍊錶的形式,那麼這種方式隨著鍊錶的長度增加,效率也凸顯出來。所以在jdk8中這塊做了優化,當鍊表超過一定長度時轉化為紅黑樹來解決這個問題,下面用流程圖畫出hashmap 的...
HashMap原始碼分析 JDK1 8
陣列 鍊錶 紅黑樹 陣列儲存元素,當有衝突時,就在該陣列位置形成乙個鍊錶,儲存衝突的元素,當鍊表元素個數大於閾值時,鍊錶就轉換為紅黑樹。transient node table transient int size int threshold final float loadfactor 預設初始容...