HashMap底層原理小記
阿新 • • 發佈:2018-08-24
return vat 閾值 ati 使用 end amp 序列化 表頭
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
首先HashMap 繼承自AbstractMap(抽象類) 實現了Map接口。
在new HashMap<K, V>()時,通過底層代碼可以知道:
public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); table = new Entry[DEFAULT_INITIAL_CAPACITY]; init(); }
其中loadFactor為float型,指hash表的負載因子;DEFAULT_INITIAL_CAPACITY 為常量,是hash表的初始容量,初值為16;threshold為int型,是hash表的裝載閾值,反應了hash表的裝載程度。接下來就會初始化一個Entry類型的數組,初始容量為16;而其中table的類型Entry是Hashmap的一個靜態內部類。
static class Entry<K,V> implements Map.Entry<K,V>,並且該類沒有無參構造函數, Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; }
當使用hashMap.put(K,V)時,
public V put(K key, V value) { if (key == null) //判斷鍵是否為null return putForNullKey(value); //添加鍵為null的值 int hash = hash(key.hashCode()); //計算二次計算key的hash值,避免沖突 int i = indexFor(hash, table.length); //根據hash值確定元素存放在表中的索引 for (Entry<K,V> e = table[i]; e != null; e = e.next) { //定位到table中的位置後,取出當前的鏈表 Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { //判斷key是否重復 V oldValue = e.value; //重復的時候返回重復key的value值 e.value = value; e.recordAccess(this); //這句的作用是什麽不知道 return oldValue; } } modCount++; // modCount是不可被序列化的,具有可見性的, //用來記錄hashmap的修改次數 addEntry(hash, key, value, i); //添加一個鍵值對 return null; }
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) //當hash表的大小>=閾值時進行擴容,hash表的大小變為原來的兩倍。 resize(2 * table.length); } Entry(int h, K k, V v, Entry<K,V> n) { value = v; //直接讓原來table[bucketIndex]位置上的表頭為新元素的next值,說明,每次插入元素都會在表位置中的表頭。 next = n; key = k; hash = h; } void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) {//當hash表的容量到達最大值時不再擴容 threshold = Integer.MAX_VALUE; return; } Entry[] newTable = new Entry[newCapacity]; transfer(newTable); //將原來hash表中的內容復制到新的hash表中 table = newTable; threshold = (int)(newCapacity * loadFactor); }
Hashmap中只允許有一個空鍵的原因:
從代碼中可以看出,當第二次添加空鍵的時候,會將原來null所對應的值覆蓋,而且,空鍵所對應的key存放在表的第一個位置table[0].通過addEntry(0, null, value, 0),在第一次添加空鍵元素的時候,可以保證null所對應的元素在table[0]鏈表的表頭。
private V putForNullKey(V value) { for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(0, null, value, 0); return null; }
HashMap底層原理小記