在Java中,HashMap是應用最廣泛的數據結構之一,它提供了一種基於鍵值對(key-value)的存儲方式,可以快速地存取、刪除和檢索數據。其中,put方法是HashMap中最主要的方法之一,本文將從多個方面深入探究HashMap.put方法實現細節。
一、put方法的使用
在Java中,使用put方法將數據放入HashMap中,具體使用方式如下:
HashMap map = new HashMap();
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);
以上代碼將三個鍵值對放入了HashMap中,即{“A”:1, “B”:2, “C”:3}。通過這種方式,就可以在HashMap中快速地存儲和查找數據了。
二、put方法的實現
HashMap的底層實現是基於數組和鏈表(或紅黑樹)的,存儲數據的時候,HashMap首先根據key的hashCode值來計算其在數組中的位置,然後將該位置上的數組元素作為鏈表頭,如果鏈表頭還沒有存儲過key-value,那麼直接放入,否則需要遍歷鏈表,找到最後一個元素後將其next指向新存儲的元素。
下面是HashMap中put方法的重要代碼實現:
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry e = table[i]; e != null; e = e.next) {
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++;
addEntry(hash, key, value, i);
return null;
}
在上面的代碼中,如果table數組還沒有初始化,需要先進行初始化。然後,根據key的hashCode值和table數組的長度,計算其在數組中的位置。接着,遍歷在該位置上的鏈表,如果找到了相同的key-value,則更新value值並返回舊值;如果沒有找到,則將新的key-value插入到鏈表的末尾。
三、關於hash方法
在put方法的實現中,需要先調用hash方法計算key的hashCode值。下面是HashMap中hash方法的代碼實現:
final int hash(Object k) {
int h = hashSeed;
if (0 != h && k instanceof String) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
在上面的代碼中,先獲取hashSeed的值,如果該值不為0且key是String類型,就調用sun.misc.Hashing.stringHash32方法來計算hashCode值;否則,直接使用key的hashCode值。
為什麼要進行h ^= (h >>> 20) ^ (h >>> 12)和h ^= (h >>> 7) ^ (h >>> 4)操作呢?這是為了使hashCode更加分散,從而減少哈希衝突的概率。上述操作使用了位運算,可以大幅提高計算效率。
四、關於擴容
當HashMap中元素數量達到了threshold(容量*負載因子)時,就會自動進行擴容操作。擴容實際上就是創建一個更大的table數組,然後將原來的元素重新分配到新數組中。下面是HashMap中resize方法的代碼實現:
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];
transfer(newTable, initHashSeedAsNeeded(newCapacity));
table = newTable;
threshold = (int)(newCapacity * loadFactor);
}
在代碼中,首先獲取原table數組的長度,然後判斷是否達到了最大容量(2的30次方)。如果已達到最大容量,則不再進行擴容。否則,創建一個新的Entry數組,調用transfer方法將原table數組中的元素重新分配到新數組中。最後將table指向新數組,同時更新threshold的值。
五、關於線程安全
HashMap並不是線程安全的,即如果多個線程同時對同一個HashMap進行操作,可能會出現不一致的結果。因此,在並發環境下應該使用ConcurrentHashMap來替代HashMap,後者是線程安全的。
六、小結
通過以上分析,我們深入探究了HashMap.put方法的實現細節。在使用HashMap的過程中,尤其要注意hash方法和擴容的相關實現,同時要在並發場景下使用線程安全的ConcurrentHashMap。
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/242169.html
微信掃一掃
支付寶掃一掃