1,hashmap的底層實現:陣列+鍊錶。
陣列的特點:遍歷查詢速度較快,但增加和刪除的代價較大。
鍊錶的特點:遍歷查詢速度較慢,但增加和刪除的代價較小。
而hashmap的底層是陣列和鍊錶,很好地整合了這兩者的優點。
2,hashmap陣列及鍊錶的產生:
陣列:當建立乙個hashmap物件時,mapmap=new hashmap();就會呼叫相應的空構造方法(hashmap有四種構造方法),返回乙個預設初始容量為16,預設載入因子為0.75的table陣列。陣列中的元素是鍊錶。
鍊錶:鍊錶產生的原因是發生了碰撞(雜湊衝突)。至於為什麼會發生碰撞呢?在向hashmap儲存資料的過程中,有兩種情況:第一種是在某一鍊錶的表頭table[bucketindex]插入鍵值對元素;另一種情況是新加入的鍵值對元素覆蓋了此前的鍵值對元素。第一種情況就會造成碰撞,從而形成鍊錶。
3,hashmap的資料結構:
hashmap的底層實現是乙個entry陣列(entry[ ] table),而且陣列中的每乙個元素都是鍊錶(由多個entry連線而成的鍊錶)。
先來看看每乙個entry的結構:
static class entryimplements map.entry
4,hashmap的重要方法實現:
(1)儲存實現put(key k, value v):
底層**:
public v put(k key, v value)
}modcount++;
addentry(hash, key, value, i);
return null;
}
hashmap的儲存分為兩種情況:
1)傳入的key引數為null:
//key==null
if (key == null)
return putfornullkey(value);
再來看看putfornullkey方法:
private v putfornullkey(v value)
}modcount++;
addentry(0, null, value, 0);
return null;
}
由此可以看到,當傳入的key為null時,就會呼叫putfornullkey方法。底層就會找到table陣列的第乙個下標的頭結點(entrye = table[0])。如果頭結點存在即不為空時,就遍歷這個鍊錶,尋找有沒有key為null的結點:如果找到了,就把傳入的value值覆蓋此前這個位置結點的value,並且返回舊的value值;如果在遍歷的過程中沒有找到key為null的結點,就執行addentry()方法。
來看看addentry方法:
void addentry(int hash, k key, v value, int bucketindex)
createentry(hash, key, value, bucketindex);
}
還有createentry方法:
void createentry(int hash, k key, v value, int bucketindex)
呼叫addentry方法後的第一步就是判斷當前容量是否超出了極限容量,如果超出了就增大陣列的長度為原來的兩倍,並且重新計算雜湊值,再呼叫createentry方法。從createentry方法中我們可以知道,entrye = table[bucketindex]語句找到了table陣列某個索引下的頭結點,在這個位置建立了乙個新的鍵值對元素。這說明,如果在遍歷的過程中沒有找到key為null的結點時,就會在這個鍊錶的表頭建立乙個新的結點元素,這個結點的key為null。
所以總結起來,當傳入的key為null時,新的鍵值對可能被儲存在table[0]的煉表頭,也可能被儲存在table[0]鏈
表中的某一結點位置。
2)傳入的key引數不為null:
來看看原始碼:
int hash = hash(key);
int i = indexfor(hash, table.length);
for (entrye = table[i]; e != null; e = e.next)
} modcount++;
addentry(hash, key, value, i);
return null;
}
當傳入的key不為null時,底層會先把key所對應的hash值求出來,再根據這個值來求出它在table陣列中的索引i。找到這個索引後,就通過entrye = table[i]來判斷這個索引下鍊錶的表頭是否為空。如果表頭不為空,則遍歷這個鍊錶,查詢鍊錶中是否存在某個元素的hash值與key值都與這個新插入的元素相匹配。如果找到,直接替換value並返回舊的value。如果找不到,就會執行頭插(在煉表頭插入這個新元素)。
經過了簡單的分析,很容易得出hashmap的儲存主要是實現兩種方式:替換和頭插。
(2)讀取實現get(key):
先來看看原始碼
public v get(object key)
final entrygetentry(object key)
return null;
}
讀取的實現和儲存有類似之處。先把key值轉化為對應的hash值,通過這個值來計算出對應在table陣列的哪個索引。如果索引處的表頭不為空,就遍歷這個鍊錶來尋找hash值、key值均匹配的元素。找得到就返回這個元素e,否則返回null。最後通過entry.getvalue()來獲取這個key所對應的值。
(3)hashmap中鍵的遍歷:
這裡只簡單地說乙個特點:遍歷出來的結果是無序的。
這和hashmap的儲存有關。最後乙個儲存的元素應該是第乙個或者最後乙個被遍歷的,但是這最後乙個儲存進去的元素可能在表頭,也可能在鍊錶中間的某乙個位置,因此就會造成這種無序性。
新手向 對各位語音識別新手的建議
由於工作的原因,很長時間不能更新部落格和管理kaldi群,每天看著kaldi群的人數不斷增長,由衷的為從事語音感到自豪,希望在我部落格和群裡能得到你們想要的,但我同時拒絕伸手黨。這幾年語音的發展很迅速,所以導致更多的人來學習 下面主要從2個方面來說明,乙個是從學生角度,乙個是從工業角度。希望以後問怎...
對 新手想說的話,對自己的回顧
純屬只是為了回顧自己知識,同時分享學到的技術 其實對於網際網路技術,也就是現在的 行業一開始並不知道多少,作為乙個小白,我在高三那一年為自己想了幾個專業,其中就包括了計算機。當然和大部分中二愛玩的普通男孩一樣是因為能夠在計算機上玩些遊戲。當時也了解一些 但懂得實在有限,老家也比較遠離繁華地帶,在當地...
談新手對CString的使用
cstring類功能強大,比stl的string類有過之無不及.新手使用cstring時,都會被它強大 的功能所吸引.然而由於對它內部機制的不了解,新手在將cstring向c的字元陣列轉換時 容易出現很多問題.因為cstring已經過載了lpctstr運算子,所以cstring類向const cha...