HashMap是Map介面的實作類
一、存盤方式
采用KV鍵值對方式存盤,基于哈希表(Hash Table)設計:
- JDK1.7 : 底層資料結構基于“陣列”+“鏈表”
- JDK1.8 : 底層資料結構基于“陣列”+“鏈表”+“紅黑樹”

當鏈表長度大于閾值(默認為8)+ 陣列長度大于64時,將鏈表轉化為紅黑樹,以減少搜索時間
二、擴容機制(擴容方法是resize()方法)
- 初始容量為16
- 加載因子為0.75:當 元素個數超過容量長度的0.75倍 時,進行擴容
- 按原有容量的2倍進行擴容
●初始容量用來規定哈希表陣列的長度
默認為16,因為16是2的整數次冪的原因,在小資料量的情況下,能減少哈希沖突,提高性能,在存盤大容量資料的時候,最好預先判斷資料量,按照2的冪次方,提前預設初始容量;●加載因子用來表示哈希表中元素的填滿程度,默認為0.75
越大則表示允許填滿的元素就越多,哈希表的空間利用率就越高,但是沖突的機會增加,反之越小則沖突的機會就會越少,但是空間很多就浪費,
三、應用特點
1.執行緒不安全
-
JDK1.7,當并發執行擴容操作時,會造成環形鏈,導致死回圈和資料丟失
-
JDK1.8,解決了環形鏈問題,但是在執行put操作時,會發生資料覆寫
2.性能高
3.允許Null key和Null value,Null Key只允許有1個,Null value 可以有多個
四、遍歷方法
1.可以采用keySet()+for回圈的方法來遍歷,keySet()回傳的是一個Key值的集合
Map<String,Integer> map=new HashMap<String,Integer>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
for(String key:map.keySet()){
system.out.println("key:"+key);
system.out.println("value:"+map.get(key));
}
2.采用EntrySet()+Iterator進行遍歷,EntrySet()回傳的是一個Map.Entry的一個集合,它提供getKey(),getValue()方法來獲取鍵值對,
Iterator< Map.Entry<String,Integer>> it=map.EntrySet().iterator();
while(it.hasNext()){
Map.Entry<String,Integer> entry=it.next();
system.out.println("key:"+entry.getKey());
system.out.println("value:"+entry.getValue());
}
3.采用EntrySet()+for回圈進行遍歷,EntrySet()回傳的是一個Map.Entry的一個集合,它提供getKey(),getValue()方法來獲取鍵值對,
Set<Entry<String, Integer>> entrySet = map.entrySet();
for (Entry<String, Integer> entry : entrySet) {
System.out.println("key"+ entry.getKey());
System.out.println("value"+entry.getValue());
}
簡寫方式:
for (Entry<String, Integer> entry : map.entrySet()) {
System.out.println("key" + entry.getKey());
System.out.printf("value"+entry.getValue());
System.out.println();
}
五、構造方法
//創建一個空的哈希表,初始容量為 16,加載因子為 0.75
public HashMap()
//創建一個空的哈希表,指定容量,使用默認的加載因子
public HashMap(int initialCapacity)
//創建一個空的哈希表,指定容量和加載因子
public HashMap(int initialCapacity, float loadFactor)
//創建一個內容為引數 m 的內容的哈希表
public HashMap(Map<? extends K, ? extends V> m)
六、put()添加方法
- 添加指定的鍵值對到 Map 中,如果已經存在,就替換 public V put(K key, V value)
邏輯如下 - 判斷陣列tab是否為空,如果為空進行初始化;
- 如果不為空,使用hash方法計算key的hashCode ,通過(n -1) & hash計算應當存放在陣列中的下標index ;
- 查看tab[index]是否存在資料;
- 如果沒有資料,就構造個Node<K,V>節點 ,存放在tab[index]中;
- 如果存在資料,說明發生哈希沖突,繼續判斷key是否相等;
- 如果相等,用新的value替換原資料;
- 如果不相等,判斷當前節點型別是不是TreeNode<K,V>樹型節點
- 如果是樹型節點,創造樹型節點插入紅黑樹中;
- 如果不是樹型節點,創建普通Node<K,V>加入鏈表尾部;
- 判斷鏈表長度大于閾值(默認為8 )并且陣列長度大于64,如果滿足,鏈表轉換為紅黑樹;如果不滿足,陣列擴容;
- 最后,插入完成之后,判斷當前節點數是否大于實際存盤空間大小;
- 如果大于,呼叫resize() ,按原陣列的長度,擴容一倍,
- JDK1.8HashMap的put方法原始碼如下:
public V put(K key, V value) {
// 對key的hashCode()做hash
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//tab為空則創建
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//計算index,并對null做處理
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//節點key存在,直接覆寫value
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//判斷該鏈為紅黑樹
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)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);
//鏈表長度大于8+陣列長度大于64時轉換為紅黑樹進行處理
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
// key已經存在直接覆寫value
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;
// 超過最大容量,就呼叫resiza()方法進行擴容
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
部分轉載
圖片來自:https://blog.csdn.net/qq_36711757/article/details/80394272?
特別希望本文可以對你有所幫助,感謝你留個贊和關注,道阻且長,我們并肩前行!
轉載請注明出處,感謝大家留言討論交流,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/258170.html
標籤:java
上一篇:Java中Map.Entry詳解
