Java集合類之HashMap源碼分析
hash表是一種常見的數據結構,主要是通過hash算法將數據盡可能的散列開來存放,當要查找某一數據時,可以通過hash算法直接定位,節省了對比查找的時間。map是一種key、value形式的鍵值對,將hash表和map結合即形成了HashMap。
在Java中HashMap的數據是以Entry
HashMap的存儲數據的示例圖如下:

HashMap 的put方法的源碼解析
public V put(K key, V value) {
if (key == null)
return putForNullKey(value); // HashMap接收key為null的數據
int hash = hash(key.hashCode()); //對key的hashCode再進行hash運算
int i = indexFor(hash, table.length);//根據hash值和entry數組的大小計算出新增數據應該存放的數組位置
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
// for循環遍歷找到的數組下標的entry,如果hash值和key都相等,則覆蓋原來的value值
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++;
//如果上面for循環沒有找到相同的hash和key,則增加一個entry
addEntry(hash, key, value, i);
return null;
}</K,V></pre> <p></p>
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry e = table[bucketIndex]; //找到下標的entry
//new 一個新的entry,賦值給當前下標數組
table[bucketIndex] = new Entry(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
} Entry(int h, K k, V v, Entry n) {
value = v;
next = n; //即將原來數組下標對應的entry賦值給新的entry的next
key = k;
hash = h;
}
(1)hash值相同且key相等數據將被覆蓋。
(2)添加新的entry時,將已存在的數據下標的entry(可能是null)賦值給新entry的next,新entry將替換原數組下標的值。
HashMap的get方法源碼解析
public V get(Object key) {
//key為null時特別處理
if (key == null)
return getForNullKey();
int hash = hash(key.hashCode());
//indexFor(hash, table.length) 根據hash值和數組長度計算出下標,然后遍歷Entry鏈表
for (Entry e = table[indexFor(hash, table.length)];
e != null;
e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
return e.value;
}
return null;
}
總結
- 一個對象當HashMap的key時,必須覆蓋hashCode()和equals()方法,hashCode()的返回值盡可能的分散。
- 當HashMap的entry的數組足夠大,key的hash值足夠分散時,即是可以實現一個entry數組下標最多只對應了一個entry,此時get方法的時間復雜度可以達到O(1)。
- 在數組長度和get方法的速度上要達到一個平衡。數組比較長碰撞出現的概率就比較小,所以get方法獲取值時就比較快,但浪費了比較多的空間;當數組長度沒有冗余時,碰撞出現的概率比較大,雖然節省了空間,但會犧牲get方法的時間。
- HashMap有默認的裝載因子loadFactor=0.75,默認的entry數組的長度為16。裝載因子的意義在于使得entry數組有冗余,默認即允許25%的冗余,當HashMap的數據的個數超過12(16*0.75)時即會對entry數組進行第一次擴容,后面的再次擴容依次類推。
- HashMap每次擴容一倍,resize時會將已存在的值從新進行數組下標的計算,這個是比較浪費時間的。在平時使用中,如果能估計出大概的HashMap的容量,可以合理的設置裝載因子loadFactor和entry數組初始長度即可以避免resize操作,提高put的效率。
- HashMap不是線程安全的,多線程環境下可以使用Hashtable或ConcurrentHashMap。
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!