在平常的開發(fā)當(dāng)中,HashMap是我最常用的Map類(沒有之一),它支持null鍵和null值,是絕大部分利用鍵值對存取場景的選。需要切記的一點(diǎn)是——HashMap不是線程安全的數(shù)據(jù)結(jié)構(gòu),所以不要在多線程場景中應(yīng)用它。
我們擁有十年網(wǎng)頁設(shè)計(jì)和網(wǎng)站建設(shè)經(jīng)驗(yàn),從網(wǎng)站策劃到網(wǎng)站制作,我們的網(wǎng)頁設(shè)計(jì)師為您提供的解決方案。為企業(yè)提供成都網(wǎng)站制作、成都網(wǎng)站設(shè)計(jì)、外貿(mào)營銷網(wǎng)站建設(shè)、微信開發(fā)、微信小程序開發(fā)、手機(jī)網(wǎng)站制作、H5開發(fā)、等業(yè)務(wù)。無論您有什么樣的網(wǎng)站設(shè)計(jì)或者設(shè)計(jì)方案要求,我們都將富于創(chuàng)造性的提供專業(yè)設(shè)計(jì)服務(wù)并滿足您的需求。通常情況下,我們使用Map的主要目的是用來放入(put)、訪問(get)或者刪除(remove),而對順序沒有特別的要求——HashMap在這種情況下就是最好的選擇。
對于HashMap來說,難理解的不在于Map,而在于Hash。
Hash,一般譯作“散列”,也有直接音譯為“哈?!钡?,這玩意什么意思呢?就是把任意長度的數(shù)據(jù)通過一種算法映射到固定長度的域上(散列值)。
再直觀一點(diǎn),就是對一串?dāng)?shù)據(jù)wang進(jìn)行雜糅,輸出另外一段固定長度的數(shù)據(jù)er——作為數(shù)據(jù)wang的特征。我們通常用一串指紋來映射某一個(gè)人,別小瞧手指頭那么大點(diǎn)的指紋,在你所處的范圍內(nèi)很難找出第二個(gè)和你相同的(人的散列算法也好厲害,有沒有?)。
對于任意兩個(gè)不同的數(shù)據(jù)塊,其散列值相同的可能性極小,也就是說,對于一個(gè)給定的數(shù)據(jù)塊,找到和它散列值相同的數(shù)據(jù)塊極為困難。再者,對于一個(gè)數(shù)據(jù)塊,哪怕只改動(dòng)它的一個(gè)比特位,其散列值的改動(dòng)也會非常的大——這正是Hash存在的價(jià)值!
在Java中,String字符串的散列值計(jì)算方法如下:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
看得懂看不懂都沒關(guān)系,我們就當(dāng)是一個(gè)“乘加迭代運(yùn)算”的算法。借此機(jī)會,我們來看一下“沉”、“默”、“王”、“二”四個(gè)字符串的散列值是多少。
String[] cmower = { "沉", "默", "王", "二" };
for (String s : cmower) {
System.out.println(s.hashCode());
}
輸出的結(jié)果如下(5位數(shù)字):
沉的散列值:27785
默的散列值:40664
王的散列值:29579
二的散列值:20108
對于HashMap來說,Hash(key,鍵位)存在的目的是為了加速鍵值對的查找(你想,如果電話薄不是按照人名的首字母排列的話,找一個(gè)人該多困難「我的微信好友有不少在昵稱前加了A,好狠」)。通常情況下,我們習(xí)慣使用String字符串來作為Map的鍵,請看以下代碼:
Map map = new HashMap<>();
String[] cmower = { "沉", "默", "王", "二" };
for (String s : cmower) {
map.put(s, s + "月入25萬");
}
那HashMap會真的會將String字符串作為實(shí)際的鍵嗎?我們來看HashMap的put方法源碼:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
雖然只有一個(gè)putVal()
方法的調(diào)用,但是你應(yīng)該已經(jīng)發(fā)現(xiàn),HashMap內(nèi)部會把key進(jìn)行一個(gè)hash運(yùn)算,具體代碼如下:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
假如key是String字符串的話,hash()
會先獲取字符串的hashCode(散列值),再對散列值進(jìn)行位于運(yùn)算,最終的值為HashMap實(shí)際的鍵(int值)。
既然HashMap在put的時(shí)候使用鍵的散列值作為實(shí)際的鍵,那么在根據(jù)鍵獲取值的時(shí)候,自然也要先對get(key)
方法的key進(jìn)行hash運(yùn)算,請看以下代碼:
public V get(Object key) {
Node e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
盡管散列值很難重復(fù),我們還是要明白,這種轉(zhuǎn)換是一種壓縮映射,也就是,散列值的空間通常遠(yuǎn)小于輸入的空間,不同的輸入可能會散列成相同的輸出。
也就是說,key1 ≠ key2,但function(key1)有可能等于function(key2)——散列值沖突了。怎么辦?
最容易想到的解決辦法就是:當(dāng)關(guān)鍵字key2的散列值value與key1的散列值value出現(xiàn)沖突時(shí),以value為基礎(chǔ),產(chǎn)生另一個(gè)散列值value1,如果value1與value不再沖突,則將value1作為key2的散列值。
依照這個(gè)辦法,總會找到不沖突的那個(gè)。
HashMap的構(gòu)造方法主要有三種:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
其中initialCapacity為初始容量(默認(rèn)為1 << 4 = 16),loadFactor為負(fù)載因子(默認(rèn)為0.75)。初始容量是HashMap在創(chuàng)建時(shí)的容量(HashMap中桶的數(shù)量);負(fù)載因子是HashMap在其容量自動(dòng)增加之前可以達(dá)到多滿的一種尺度。
當(dāng)HashMap中的條目數(shù)超出了負(fù)載因子與當(dāng)前容量的乘積時(shí),則要對HashMap擴(kuò)容,增加大約兩倍的桶數(shù)。
通常,默認(rèn)的負(fù)載因子 (0.75) 是時(shí)間和空間成本上的一種折衷。負(fù)載因子過高雖然減少了空間開銷,但同時(shí)增加了查詢成本。如果負(fù)載因子過小,則初始容量要增大,否則會導(dǎo)致頻繁的擴(kuò)容。
在設(shè)置初始容量時(shí)應(yīng)該考慮到映射中所需的條目數(shù)及其加載因子,以便大限度地減少擴(kuò)容的操作次數(shù)。
如果能夠提前預(yù)知要存取的鍵值對數(shù)量的話,可以考慮設(shè)置合適的初始容量(大于“預(yù)估元素?cái)?shù)量 / 負(fù)載因子”,并且是2的冪數(shù))。
在之前很長的一段時(shí)間內(nèi),我對HashMap的認(rèn)知僅限于會用它的put(key, value)
和value = get(key)
。
但,當(dāng)我強(qiáng)迫自己每周要輸出一篇Java方面的技術(shù)文章后,我對HashMap真的“深入淺出”了——散列值(哈希值)、散列沖突(哈希沖突)、初始容量和負(fù)載因子,竟然能站在我面前一直笑——而原先,我見到這些關(guān)鍵字就逃之夭夭了,我怕見到它們。
上一篇:Java 集合類入門篇
下一篇:Java泛型的重要目的:別讓貓別站在狗隊(duì)里
微信搜索「沉默王二」公眾號,關(guān)注后回復(fù)「免費(fèi)視頻」獲取 500G 高質(zhì)量教學(xué)視頻(已分門別類)。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場景需求。