HashMap

1.構(gòu)造函數(shù)

//默認(rèn)size=16,默認(rèn)負(fù)載因子0.75

1.1?

public HashMap() {

this.loadFactor = DEFAULT_LOAD_FACTOR;// all other fields defaulted

}

1.2

//map容量,負(fù)載因子

public HashMap(int initialCapacity,float loadFactor) {

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;

this.threshold = tableSizeFor(initialCapacity);

}

/**

* Returns a power of two size for the given target capacity.

* 為了控制hashmap的容量一定是2的n次方

* 通過位移運算弃秆,找到大于或等于 cap 的 最小2的n次冪饲宛。位運算的速度要優(yōu)于乘除加減

*/

static final int tableSizeFor(int cap) {

int n = cap -1;

n |= n >>>1;

n |= n >>>2;

n |= n >>>4;

n |= n >>>8;

n |= n >>>16;

return (n <0) ?1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n +1;

}

2.put數(shù)據(jù)

2.1?

public V put(K key, V value) {

return putVal(hash(key), key, value,false,true);

}

2.2

static final int hash(Object key) {

int h;

// h >>> 16 為了讓高位參與運算泼橘,使得更均勻

// (h = key.hashCode()) ^ (h >>> 16)

// 先計算key的hash值择份,然后再用哈希值與自己右移16位做異或運算

// 使用異或 ^運算符 使得運算結(jié)果更有隨機性蘸嘶,異或運算只有不同才是1

// PS:因為或運算履植,值會更偏向于1计雌,與運算,值會更偏向于0

? ? return (key ==null) ?0 : (h = key.hashCode()) ^ (h >>>16);

}

2.3

final V putVal(int hash, K key, V value,boolean onlyIfAbsent,

boolean evict) {

Node[] tab; Node p;int n, i;

//第一次put數(shù)據(jù)的時候玫霎,hashmap才會初始化凿滤,并不是在構(gòu)造函數(shù)中

? ? if ((tab = table) ==null || (n = tab.length) ==0)

n = (tab = resize()).length;

//由于容量是2的n次方妈橄,所以 (n - 1) & hash 其實等同于 n % hash

//而且這樣運算的效率會更高

? ? if ((p = tab[i = (n -1) & hash]) ==null)

tab[i] = newNode(hash, key, value,null);

else {

Node e; K k;

if (p.hash == hash &&

((k = p.key) == key || (key !=null && key.equals(k))))

e = p;

else if (pinstanceof TreeNode)

e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);

else {

for (int binCount =0; ; ++binCount) {

if ((e = p.next) ==null) {

p.next = newNode(hash, key, value,null);

if (binCount >= TREEIFY_THRESHOLD -1)// -1 for 1st

? ? ? ? ? ? ? ? ? ? ? ? treeifyBin(tab, hash);

break;

}

if (e.hash == hash &&

((k = e.key) == key || (key !=null && key.equals(k))))

break;

p = e;

}

}

if (e !=null) {// existing mapping for key

? ? ? ? ? ? V oldValue = e.value;

if (!onlyIfAbsent || oldValue ==null)

e.value = value;

afterNodeAccess(e);

return oldValue;

}

}

++modCount;

if (++size > threshold)

resize();

afterNodeInsertion(evict);

return null;

}

2.4

final Node[] resize() {

Node[] oldTab = table;

int oldCap = (oldTab ==null) ?0 : oldTab.length;

//舊的擴容閾值

? ? int oldThr = threshold;

//新的容量和擴容閾值

? ? int newCap, newThr =0;

//說明已經(jīng)是舊的hashmap可進(jìn)行resize

? ? if (oldCap >0) {

//hashmap >=最大容量

? ? ? ? if (oldCap >= MAXIMUM_CAPACITY) {

//擴容閾值=Integer.max

? ? ? ? ? ? threshold = Integer.MAX_VALUE;

return oldTab;

}

//容量擴容1倍 后小于最大容量 且? 老容量 >= 初始容量

//為什么擴容一倍大小呢?

//擴容一倍大小的原因是

// (1)為了保證hash 到數(shù)組位置的效率

// (2)關(guān)系到擴容后元素在newCap中的放置問題:

? ? ? ? else if ((newCap = oldCap <<1) < MAXIMUM_CAPACITY &&

oldCap >= DEFAULT_INITIAL_CAPACITY)

//擴容閾值也擴大1倍

? ? ? ? ? ? newThr = oldThr <<1;// double threshold

? ? }

else if (oldThr >0)// initial capacity was placed in threshold

? ? ? ? newCap = oldThr;

else {

// zero initial threshold signifies using defaults

// 如果是new 了一個新的hashmap的時候翁脆,會走這一行眷蚓,初始化容量和擴容閾值

? ? ? ? newCap = DEFAULT_INITIAL_CAPACITY;

newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);

}

//1.第一次初始化的時候不是0了已經(jīng)

? ? if (newThr ==0) {

float ft = (float)newCap * loadFactor;

newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?

(int)ft : Integer.MAX_VALUE);

}

threshold = newThr;

@SuppressWarnings({"rawtypes","unchecked"})

Node[] newTab = (Node[])new Node[newCap];

table = newTab;

//如果不是初始化進(jìn)來的

? ? if (oldTab !=null) {

//要遍歷所有的節(jié)點

? ? ? ? for (int j =0; j < oldCap; ++j) {

Node e;

//節(jié)點為null就continue 不為空的時候,把節(jié)點給臨時節(jié)點e

? ? ? ? ? ? if ((e = oldTab[j]) !=null) {

//把老結(jié)點 置為null

? ? ? ? ? ? ? ? oldTab[j] =null;

//如果該結(jié)點沒有next反番,也就是沒有發(fā)生過沖突沙热,桶的位置只有一個元素,把節(jié)點放入新的table中

? ? ? ? ? ? ? ? if (e.next ==null)

//就把值直接扔到對應(yīng)的位置罢缸。這里有個問題篙贸,沒有發(fā)生過沖突的,就直接放進(jìn)去了枫疆,擴容后也不會發(fā)生沖突嗎

// 爵川,感覺這個很nb啊

? ? ? ? ? ? ? ? ? ? newTab[e.hash & (newCap -1)] = e;

//如果節(jié)點發(fā)生過沖突,并且是紅黑樹的數(shù)據(jù)結(jié)構(gòu)

? ? ? ? ? ? ? ? else if (einstanceof TreeNode)

//說明沖突的個數(shù)大于8個息楔,那么就把樹切割開

? ? ? ? ? ? ? ? ? ? ((TreeNode)e).split(this, newTab, j, oldCap);

else {// preserve order

? ? ? ? ? ? ? ? ? ? Node loHead =null, loTail =null;

Node hiHead =null, hiTail =null;

Node next;

do {

next = e.next;

if ((e.hash & oldCap) ==0) {

if (loTail ==null)

loHead = e;

else

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? loTail.next = e;

loTail = e;

}

else {

if (hiTail ==null)

hiHead = e;

else

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hiTail.next = e;

hiTail = e;

}

}while ((e = next) !=null);

if (loTail !=null) {

loTail.next =null;

newTab[j] = loHead;

}

if (hiTail !=null) {

hiTail.next =null;

newTab[j + oldCap] = hiHead;

}

}

}

}

}

return newTab;

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末雁芙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子钞螟,更是在濱河造成了極大的恐慌兔甘,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鳞滨,死亡現(xiàn)場離奇詭異洞焙,居然都是意外死亡,警方通過查閱死者的電腦和手機拯啦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門澡匪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人褒链,你說我怎么就攤上這事唁情。” “怎么了甫匹?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵甸鸟,是天一觀的道長。 經(jīng)常有香客問我兵迅,道長抢韭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任恍箭,我火速辦了婚禮刻恭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘扯夭。我一直安慰自己鳍贾,他們只是感情好鞍匾,可當(dāng)我...
    茶點故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著骑科,像睡著了一般橡淑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上纵散,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天梳码,我揣著相機與錄音,去河邊找鬼伍掀。 笑死掰茶,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蜜笤。 我是一名探鬼主播濒蒋,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼把兔!你這毒婦竟也來了沪伙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤县好,失蹤者是張志新(化名)和其女友劉穎围橡,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體缕贡,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡翁授,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了晾咪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片收擦。...
    茶點故事閱讀 39,932評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖谍倦,靈堂內(nèi)的尸體忽然破棺而出塞赂,到底是詐尸還是另有隱情,我是刑警寧澤昼蛀,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布宴猾,位于F島的核電站,受9級特大地震影響曹洽,放射性物質(zhì)發(fā)生泄漏鳍置。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一送淆、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧怕轿,春花似錦偷崩、人聲如沸辟拷。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衫冻。三九已至,卻和暖如春谒出,著一層夾襖步出監(jiān)牢的瞬間隅俘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工笤喳, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留为居,地道東北人。 一個月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓杀狡,卻偏偏與公主長得像蒙畴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子呜象,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,884評論 2 354