Java源碼角度分析HashMap用法
—HashMap—
優(yōu)點(diǎn):超級(jí)快速的查詢(xún)速度,時(shí)間復(fù)雜度可以達(dá)到O(1)的數(shù)據(jù)結(jié)構(gòu)非HashMap莫屬。動(dòng)態(tài)的可變長(zhǎng)存儲(chǔ)數(shù)據(jù)(相對(duì)于數(shù)組而言)。
缺點(diǎn):需要額外計(jì)算一次hash值,如果處理不當(dāng)會(huì)占用額外的空間。
—HashMap如何使用—
平時(shí)我們使用hashmap如下
Map<Integer,String> maps=new HashMap<Integer,String>(); maps.put(1, "a"); maps.put(2, "b");
上面代碼新建了一個(gè)HashMap并且插入了兩個(gè)數(shù)據(jù),這里不接受基本數(shù)據(jù)類(lèi)型來(lái)做K,V
如果這么寫(xiě)的話(huà),就會(huì)出問(wèn)題了:
Map<int,double> maps=new HashMap<int,double>();
我們?yōu)槭裁匆@樣使用呢?請(qǐng)看源碼:
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
這是HashMap實(shí)現(xiàn)類(lèi)的定義。
—HashMap是一個(gè)動(dòng)態(tài)變長(zhǎng)的數(shù)據(jù)結(jié)構(gòu)—
在使用HashMap的時(shí)候,為了提高執(zhí)行效率,我們往往會(huì)設(shè)置HashMap初始化容量:
Map<String,String> rm=new HashMap<String,String>(2)
或者使用guava的工具類(lèi)Maps,可以很方便的創(chuàng)建一個(gè)集合,并且,帶上合適的大小初始化值。
Map<String, Object> map = Maps.newHashMapWithExpectedSize(7);
那么為什么要這樣使用呢?我們來(lái)看他們的源碼構(gòu)造函數(shù)。
未帶參的構(gòu)造函數(shù):
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
/**
* Constructs an empty <tt>HashMap</tt> with the specified initial
* capacity and the default load factor (0.75).
*
* @param initialCapacity the initial capacity.
* @throws IllegalArgumentException if the initial capacity is negative.
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
名詞解釋?zhuān)?/p>
DEFAULT_LOAD_FACTOR //默認(rèn)加載因子,如果不制定的話(huà)是0.75 DEFAULT_INITIAL_CAPACITY //默認(rèn)初始化容量,默認(rèn)是16 threshold //閾(yu)值 根據(jù)加載因子和初始化容量計(jì)算得出 ,<span style="color: rgb(54, 46, 43); font-family: "microsoft yahei";">threshold表示當(dāng)HashMap的size大于threshold時(shí)會(huì)執(zhí)行resize操作。
因此我們知道了,如果我們調(diào)用無(wú)參數(shù)的構(gòu)造方法的話(huà),我們將得到一個(gè)16容量的數(shù)組。
所以問(wèn)題就來(lái)了:如果初始容量不夠怎么辦?
數(shù)組是定長(zhǎng)的,如何用一個(gè)定長(zhǎng)的數(shù)據(jù)來(lái)表示一個(gè)不定長(zhǎng)的數(shù)據(jù)呢,答案就是找一個(gè)更長(zhǎng)的,但是在resize的時(shí)候是很降低效率的。所以我們建議HashMap的初始化的時(shí)候要給一個(gè)靠譜的容量大小。
—HashMap的Put方法—
public V put(K key, V value) {
if (key == null) //鍵為空的情況,HashMap和HashTable的一個(gè)區(qū)別
return putForNullKey(value);
int hash = hash(key.hashCode()); //根據(jù)鍵的hashCode算出hash值
int i = indexFor(hash, table.length); //根據(jù)hash值算出究竟該放入哪個(gè)數(shù)組下標(biāo)中
for (Entry<K,V> e = table[i]; e != null; e = e.next) {//整個(gè)for循環(huán)實(shí)現(xiàn)了如果存在K那么就替換V
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;//計(jì)數(shù)器
addEntry(hash, key, value, i); //添加到數(shù)組中
return null;
}
如果插入的數(shù)據(jù)超過(guò)現(xiàn)有容量就會(huì)執(zhí)行
addEntry(hash, key, value, i);
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
<span style="color:#ff0000;"><strong> resize(2 * table.length);
}
這里顯示了如果當(dāng)前 size++ >threshold 的話(huà)那么就會(huì)擴(kuò)展當(dāng)前的size的兩倍,執(zhí)行resize(2*table.length),那么他們是如何擴(kuò)展的呢?
void resize(int newCapacity) {
Entry[] oldTable = table;
int oldCapacity = oldTable.length;
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return;
}
Entry[] newTable = new Entry[newCapacity]; <span style="color: rgb(51, 51, 51); font-family: Arial;">new 一個(gè)新的數(shù)組,</span>
<strong> <span style="color:#ff0000;">transfer(newTable);</span> </strong> //將就數(shù)組轉(zhuǎn)移到新的數(shù)組中
table = newTable;
threshold = (int)(newCapacity * loadFactor); //重新計(jì)算容量
}
對(duì)于轉(zhuǎn)移數(shù)組transfer是如何轉(zhuǎn)移的呢?
void transfer(Entry[] newTable) {
Entry[] src = table;
int newCapacity = newTable.length;
for (int j = 0; j < src.length; j++) {
Entry<K,V> e = src[j];
if (e != null) {
src[j] = null;
do {
Entry<K,V> next = e.next;
int i = <strong><span style="color:#ff0000;">indexFor(e.hash, newCapacity); //根據(jù)hash值個(gè)容量重新計(jì)算下標(biāo)</span></strong>
e.next = newTable[i];
newTable[i] = e;
e = next;
} while (e != null);
}
}
}
—hashmap擴(kuò)容額外執(zhí)行次數(shù)—
因此如果我們要添加一個(gè)1000個(gè)元素的hashMap,如果我們用默認(rèn)值那么我么需要額外的計(jì)算多少次呢
當(dāng)大于16*0.75=12的時(shí)候,需要從新計(jì)算12次
當(dāng)大于16*2*0.75=24的時(shí)候,需要額外計(jì)算24次
……
當(dāng)大于16*n*0.75=768的時(shí)候,需要額外計(jì)算768次
所以我們總共在擴(kuò)充過(guò)程中額外計(jì)算12+24+48+……+768次
因此強(qiáng)力建議我們?cè)陧?xiàng)目中如果知道范圍的情況下,我們應(yīng)該手動(dòng)指定初始大小像這樣:
Map<Integer,String> maps=new HashMap<Integer,String>(1000);
總結(jié):這就是為什么當(dāng)hashmap使用過(guò)程中如果超出 初始容量后他的執(zhí)行效率嚴(yán)重下降的原因。
以上就是本文關(guān)于Java源碼角度分析HashMap用法的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專(zhuān)題,如有不足之處,歡迎留言指出。感謝朋友們對(duì)本站的支持!
- 剖析Java中HashMap數(shù)據(jù)結(jié)構(gòu)的源碼及其性能優(yōu)化
- 在Java8與Java7中HashMap源碼實(shí)現(xiàn)的對(duì)比
- 深入理解Java之HashMap源碼剖析
- Java集合系列之HashMap源碼分析
- Java源碼解析HashMap的keySet()方法
- Java源碼解析HashMap的resize函數(shù)
- Java源碼解析HashMap的tableSizeFor函數(shù)
- Java源碼解析HashMap成員變量
- Java源碼解析HashMap簡(jiǎn)介
- java基礎(chǔ)類(lèi)型源碼解析之多角度講HashMap
相關(guān)文章
Java?C++刷題leetcode1106解析布爾表達(dá)式
這篇文章主要為大家介紹了Java?C++刷題leetcode1106解析布爾表達(dá)式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
Spring用AspectJ開(kāi)發(fā)AOP(基于A(yíng)nnotation)
這篇文章主要介紹了Spring用AspectJ開(kāi)發(fā)AOP(基于A(yíng)nnotation),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
Jpa 如何使用@EntityListeners 實(shí)現(xiàn)實(shí)體對(duì)象的自動(dòng)賦值
這篇文章主要介紹了Jpa 如何使用@EntityListeners 實(shí)現(xiàn)實(shí)體對(duì)象的自動(dòng)賦值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Java動(dòng)態(tài)規(guī)劃篇之線(xiàn)性DP的示例詳解
這篇文章主要通過(guò)幾個(gè)例題為大家詳細(xì)介紹一些Java動(dòng)態(tài)規(guī)劃中的線(xiàn)性DP,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Java有一定的幫助,需要的可以參考一下2022-11-11
MybatisPlus更新為null的字段及自定義sql注入
mybatis-plus在執(zhí)行更新操作,當(dāng)更新字段為空字符串或者null的則不會(huì)執(zhí)行更新,本文主要介紹了MybatisPlus更新為null的字段及自定義sql注入,感興趣的可以了解一下2024-05-05
Java 多線(xiàn)程并發(fā)AbstractQueuedSynchronizer詳情
這篇文章主要介紹了Java 多線(xiàn)程并發(fā)AbstractQueuedSynchronizer詳情,文章圍繞主題展開(kāi)想象的內(nèi)容介紹,具有一定的參考價(jià)值,感興趣的小伙伴可以參考一下2022-06-06
IDEA啟動(dòng)Tomcat報(bào)Unrecognized option: --add-opens=java
這篇文章主要為大家介紹了解決IDEA啟動(dòng)Tomcat報(bào)Unrecognized option: --add-opens=java.base/java.lang=ALL-UNNAMED的方法,文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-08-08
idea hibernate jpa 生成實(shí)體類(lèi)的實(shí)現(xiàn)
這篇文章主要介紹了idea hibernate jpa 生成實(shí)體類(lèi)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11

