Java HashMap原始碼詳解
阿新 • • 發佈:2019-02-08
package java.util; import java.io.*; public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { // 系統預設初始容量,必須是2的n次冪,這是出於優化考慮的 static final int DEFAULT_INITIAL_CAPACITY = 16; // 系統預設最大容量 static final int MAXIMUM_CAPACITY = 1 << 30; // 系統預設負載因子,可在建構函式中指定 static final float DEFAULT_LOAD_FACTOR = 0.75f; // 用於儲存的表,長度可以調整,且必須是2的n次冪 transient Entry[] table; // 當前map的key-value對映數,也就是當前size transient int size; // 閾值 int threshold; // 雜湊表的負載因子 final float loadFactor; // 用於確保使用迭代器的時候,HashMap並未進行更改 transient volatile int modCount; // 構造一個帶指定初始容量和載入因子的空 HashMap。 public HashMap(int initialCapacity, float loadFactor) { // 如果指定初始容量小於0,拋錯 if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); // 如果初始容量大於系統預設最大容量,則初始容量為最大容量 if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; // 如果loadFactor小於0,或loadFactor是NaN,則拋錯 if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); // 尋找一個2的k次冪capacity恰好大於initialCapacity int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; // 設定載入因子 this.loadFactor = loadFactor; // 設定閾值為capacity * loadFactor,實際上當HashMap當前size到達這個閾值時,HashMap就需要擴大一倍了。 threshold = (int)(capacity * loadFactor); // 建立一個capacity長度的陣列用於儲存資料 table = new Entry[capacity]; // 開始初始化 init(); } // 構造一個帶指定初始容量和預設載入因子 (0.75) 的空 HashMap。 public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } // 構造一個具有預設初始容量 (16) 和預設載入因子 (0.75) 的空 HashMap。 public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); table = new Entry[DEFAULT_INITIAL_CAPACITY]; init(); } // 構造一個對映關係與指定 Map 相同的新 HashMap。 public HashMap(Map<? extends K, ? extends V> m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); putAllForCreate(m); } // 內部公用工具 // 定義一個空方法用於未來的子物件擴充套件,該方法用於初始化之後,插入元素之前 void init() { } // 預處理hash值,避免較差的離散hash序列,導致桶沒有充分利用 static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } // 返回對應hash值得索引 static int indexFor(int h, int length) { /***************** * 由於length是2的n次冪,所以h & (length-1)相當於h % length。 * 對於length,其2進製表示為1000...0,那麼length-1為0111...1。 * 那麼對於任何小於length的數h,該式結果都是其本身h。 * 對於h = length,該式結果等於0。 * 對於大於length的數h,則和0111...1位與運算後, * 比0111...1高或者長度相同的位都變成0, * 相當於減去j個length,該式結果是h-j*length, * 所以相當於h % length。 * 其中一個很常用的特例就是h & 1相當於h % 2。 * 這也是為什麼length只能是2的n次冪的原因,為了優化。 */ return h & (length-1); } // 返回當前map的key-value對映數,也就是當前size public int size() { return size; } // 該HashMap是否是空的,如果size為0,則為空 public boolean isEmpty() { return size == 0; } // 返回指定鍵所對映的值;如果對於該鍵來說,此對映不包含任何對映關係,則返回 null public V get(Object key) { // 如果key為null if (key == null) return getForNullKey(); // 對hashCode值預處理 int hash = hash(key.hashCode()); // 得到對應的hash值的桶,如果這個桶不是,就通過next獲取下一個桶 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; // 如果hash值相等,並且key相等則證明這個桶裡的東西是我們想要的 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } // 所有桶都找遍了,沒找到想要的,所以返回null return null; } // 如果要得到key為null的值,則通過這個獲取 private V getForNullKey() { // 遍歷table[0]裡的所有桶 for (Entry<K,V> e = table[0]; e != null; e = e.next) { // 看看桶的key是不是null,是則返回相應值 if (e.key == null) return e.value; } // 沒找到返回null return null; } // 如果此對映包含對於指定鍵的對映關係,則返回true public boolean containsKey(Object key) { return getEntry(key) != null; } // 通過key獲取一個value final Entry<K,V> getEntry(Object key) { // 如果key為null,則hash為0,否則用hash函式預處理 int hash = (key == null) ? 0 : hash(key.hashCode()); // 得到對應的hash值的桶,如果這個桶不是,就通過next獲取下一個桶 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; // 如果hash值相等,並且key相等則證明這個桶裡的東西是我們想要的 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } // 所有桶都找遍了,沒找到想要的,所以返回null return null; } // 在此對映中關聯指定值與指定鍵。如果該對映以前包含了一個該鍵的對映關係,則舊值被替換 public V put(K key, V value) { // 如果key為null使用putForNullKey來獲取 if (key == null) return putForNullKey(value); // 使用hash函式預處理hashCode int hash = hash(key.hashCode()); // 獲取對應的索引 int i = indexFor(hash, table.length); // 得到對應的hash值的桶,如果這個桶不是,就通過next獲取下一個桶 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; // 如果hash相同並且key相同 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { // 獲取當前的value V oldValue = e.value; // 將要儲存的value存進去 e.value = value; e.recordAccess(this); // 返回舊的value return oldValue; } } modCount++; addEntry(hash, key, value, i); return null; } // key為null怎麼放value private V putForNullKey(V value) { // 遍歷table[0]的所有桶 for (Entry<K,V> e = table[0]; e != null; e = e.next) { // 如果key是null if (e.key == null) { // 取出oldValue,並存入value V oldValue = e.value; e.value = value; e.recordAccess(this); // 返回oldValue return oldValue; } } modCount++; addEntry(0, null, value, 0); return null; } // 看看需不需要建立新的桶 private void putForCreate(K key, V value) { // 如果key為null,則定義hash為0,否則用hash函式預處理 int hash = (key == null) ? 0 : hash(key.hashCode()); // 獲取對應的索引 int i = indexFor(hash, table.length); // 遍歷所有桶 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; // 如果有hash相同,且key相同,那麼則不需要建立新的桶,退出 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { e.value = value; return; } } // 否則需要建立新的桶 createEntry(hash, key, value, i); } // 根據Map建立所有對應的桶 private void putAllForCreate(Map<? extends K, ? extends V> m) { for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) { Map.Entry<? extends K, ? extends V> e = i.next(); putForCreate(e.getKey(), e.getValue()); } } // 更具新的容量來resize這個HashMap void resize(int newCapacity) { // 儲存oldTable Entry[] oldTable = table; // 儲存舊的容量 int oldCapacity = oldTable.length; // 如果舊的容量已經是系統預設最大容量了,那麼將閾值設定成整形的最大值,退出 if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } // 根據新的容量新建一個table Entry[] newTable = new Entry[newCapacity]; // 將table轉換成newTable transfer(newTable); // 將table設定newTable table = newTable; // 設定閾值 threshold = (int)(newCapacity * loadFactor); } // 將所有格子裡的桶都放到新的table中 void transfer(Entry[] newTable) { // 得到舊的table Entry[] src = table; // 得到新的容量 int newCapacity = newTable.length; // 遍歷src裡面的所有格子 for (int j = 0; j < src.length; j++) { // 取到格子裡的桶(也就是連結串列) Entry<K,V> e = src[j]; // 如果e不為空 if (e != null) { // 將當前格子設成null src[j] = null; // 遍歷格子的所有桶 do { // 取出下個桶 Entry<K,V> next = e.next; // 尋找新的索引 int i = indexFor(e.hash, newCapacity); // 設定e.next為newTable[i]儲存的桶(也就是連結串列連線上) e.next = newTable[i]; // 將e設成newTable[i] newTable[i] = e; // 設定e為下一個桶 e = next; } while (e != null); } } } // 將指定對映的所有對映關係複製到此對映中,這些對映關係將替換此對映目前針對指定對映中所有鍵的所有對映關係 public void putAll(Map<? extends K, ? extends V> m) { // 看看需要複製多少個對映關係 int numKeysToBeAdded = m.size(); if (numKeysToBeAdded == 0) return; // 如果要複製的對映關係比閾值還要多 if (numKeysToBeAdded > threshold) { // 重新計算新的容量先resize int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1); if (targetCapacity > MAXIMUM_CAPACITY) targetCapacity = MAXIMUM_CAPACITY; int newCapacity = table.length; while (newCapacity < targetCapacity) newCapacity <<= 1; if (newCapacity > table.length) resize(newCapacity); } // 迭代將key-value對映放進該HashMap for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) { Map.Entry<? extends K, ? extends V> e = i.next(); put(e.getKey(), e.getValue()); } } // 從此對映中移除指定鍵的對映關係(如果存在) public V remove(Object key) { Entry<K,V> e = removeEntryForKey(key); return (e == null ? null : e.value); } // 根據key刪除桶,並返回對應value final Entry<K,V> removeEntryForKey(Object key) { int hash = (key == null) ? 0 : hash(key.hashCode()); int i = indexFor(hash, table.length); // 找到對應的格子 Entry<K,V> prev = table[i]; Entry<K,V> e = prev; // 遍歷所有桶 while (e != null) { Entry<K,V> next = e.next; Object k; // 如果找到對應的桶 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { modCount++; // size減1 size--; // 如果第一個就是要刪的桶 if (prev == e) // 則table[i]等於下一個桶 table[i] = next; else // 否則上一個桶的下一個等於下一個桶 prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; } return e; } // 根據桶來刪除map裡的值 final Entry<K,V> removeMapping(Object o) { // 如果o不是Map.Entry的例項,則退出返回null if (!(o instanceof Map.Entry)) return null; // 將o轉成Map.Entry Map.Entry<K,V> entry = (Map.Entry<K,V>) o; // 得到他的key Object key = entry.getKey(); // 得到對應的hash int hash = (key == null) ? 0 : hash(key.hashCode()); // 得到對應的索引 int i = indexFor(hash, table.length); Entry<K,V> prev = table[i]; Entry<K,V> e = prev; // 遍歷所有桶 while (e != null) { Entry<K,V> next = e.next; // 如果找到對應的桶,則刪掉它 if (e.hash == hash && e.equals(entry)) { modCount++; size--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; } // 並返回該桶 return e; } // 從此對映中移除所有對映關係。此呼叫返回後,對映將為空 public void clear() { modCount++; Entry[] tab = table; // 遍歷table中的所有格子,然偶後設為null for (int i = 0; i < tab.length; i++) tab[i] = null; // 設定size為0 size = 0; } // 如果此對映將一個或多個鍵對映到指定值,則返回 true public boolean containsValue(Object value) { // 如果value為空,則返回containsNullValue函式的返回值 if (value == null) return containsNullValue(); Entry[] tab = table; // 遍歷table所有格子(連結串列) for (int i = 0; i < tab.length ; i++) // 遍歷連結串列中的每個桶 for (Entry e = tab[i] ; e != null ; e = e.next) // 如果值相同,則返回true if (value.equals(e.value)) return true; // 否則返回false return false; } // 對value為null的處理,這裡沒什麼特別的 private boolean containsNullValue() { Entry[] tab = table; for (int i = 0; i < tab.length ; i++) for (Entry e = tab[i] ; e != null ; e = e.next) if (e.value == null) return true; return false; } // 返回此 HashMap 例項的淺表副本:並不複製鍵和值本身 public Object clone() { HashMap<K,V> result = null; try { result = (HashMap<K,V>)super.clone(); } catch (CloneNotSupportedException e) { // assert false; } result.table = new Entry[table.length]; result.entrySet = null; result.modCount = 0; result.size = 0; result.init(); result.putAllForCreate(this); return result; } // 內建class輸入物件,也就是我們說的桶 static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; Entry<K,V> next; final int hash; // 建構函式 Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; } // 返回key public final K getKey() { return key; } // 返回value public final V getValue() { return value; } // 設定value public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } // 是否相同 public final boolean equals(Object o) { // 如果o不是Map.Entry的例項,那麼肯定不相同了 if (!(o instanceof Map.Entry)) return false; // 將o轉成Map.Entry Map.Entry e = (Map.Entry)o; // 得到key和value對比是否相同,相同則為true Object k1 = getKey(); Object k2 = e.getKey(); if (k1 == k2 || (k1 != null && k1.equals(k2))) { Object v1 = getValue(); Object v2 = e.getValue(); if (v1 == v2 || (v1 != null && v1.equals(v2))) return true; } // 否則為false return false; } // hashCode public final int hashCode() { return (key==null ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode()); } // 返回String public final String toString() { return getKey() + "=" + getValue(); } // 使用該方法證明該key已經在該map中 void recordAccess(HashMap<K,V> m) { } // 該方法記錄該key已經被移除了 void recordRemoval(HashMap<K,V> m) { } } // 新增一個新的桶來儲存該key和value void addEntry(int hash, K key, V value, int bucketIndex) { // 儲存對應table的值 Entry<K,V> e = table[bucketIndex]; // 然後用新的桶套住舊的桶,連結串列 table[bucketIndex] = new Entry<K,V>(hash, key, value, e); // 如果當前size大於等於閾值 if (size++ >= threshold) // 調整容量 resize(2 * table.length); } // 新建一個桶,該方法不需要判斷是否超過閾值 void createEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K,V>(hash, key, value, e); size++; } // 內部class HashIterator迭代器 private abstract class HashIterator<E> implements Iterator<E> { Entry<K,V> next; // 下一個桶 int expectedModCount; // 保護HashMap沒有變更 int index; // 當前的索引 Entry<K,V> current; // 當前的桶 // 構造方法 HashIterator() { // 儲存modCount,因為如果HashMap進行了任何操作modCount都會增加,所以如果發現modCount變化了,就可以丟擲失敗 expectedModCount = modCount; // 進入第一個桶 if (size > 0) { Entry[] t = table; while (index < t.length && (next = t[index++]) == null) ; } } // 看看有沒有下一個桶 public final boolean hasNext() { return next != null; } // 獲取下一個桶 final Entry<K,V> nextEntry() { // modCount變化了,丟擲失敗 if (modCount != expectedModCount) throw new ConcurrentModificationException(); // 得到next Entry<K,V> e = next; // 如果next為空,丟擲失敗 if (e == null) throw new NoSuchElementException(); // 如果next.next為空,將next定義為下一個格子中的桶,否則為該格子的下一個桶 if ((next = e.next) == null) { Entry[] t = table; while (index < t.length && (next = t[index++]) == null) ; } // 給current賦值 current = e; // 返回e return e; } // 刪除 public void remove() { // 如果當前為空,丟擲 if (current == null) throw new IllegalStateException(); // modCount變化了,丟擲失敗 if (modCount != expectedModCount) throw new ConcurrentModificationException(); // 獲得當前的key Object k = current.key; // 設定current為null current = null; // 刪除掉對應key的元素 HashMap.this.removeEntryForKey(k); // 重置expectedModCount expectedModCount = modCount; } } // 內部class ValueIterator迭代器,我們可以看到修改了next方法 private final class ValueIterator extends HashIterator<V> { public V next() { return nextEntry().value; } } // 內部class KeyIterator迭代器,我們可以看到修改了next方法 private final class KeyIterator extends HashIterator<K> { public K next() { return nextEntry().getKey(); } } // 內部class EntryIterator迭代器,我們可以看到修改了next方法 private final class EntryIterator extends HashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } } // 定義對應的 iterator() 方法 Iterator<K> newKeyIterator() { return new KeyIterator(); } Iterator<V> newValueIterator() { return new ValueIterator(); } Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); } private transient Set<Map.Entry<K,V>> entrySet = null; /** * 返回此對映中所包含的鍵的 Set 檢視。 * 該 set 受對映的支援,所以對對映的更改將反映在該 set 中, * 反之亦然。如果在對 set 進行迭代的同時修改了對映(通過迭代器自己的 remove 操作除外), * 則迭代結果是不確定的。該 set 支援元素的移除,通過 * Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作 * 可從該對映中移除相應的對映關係。它不支援 add 或 addAll 操作。 */ public Set<K> keySet() { Set<K> ks = keySet; // 如果keySet為空,則通過新建一個KeySet return (ks != null ? ks : (keySet = new KeySet())); } // 內部類KeySet private final class KeySet extends AbstractSet<K> { // 定義iterator方法 public Iterator<K> iterator() { return newKeyIterator(); } // 定義size public int size() { return size; } // 定義contains public boolean contains(Object o) { return containsKey(o); } // 定義remove public boolean remove(Object o) { return HashMap.this.removeEntryForKey(o) != null; } // 定義clear public void clear() { HashMap.this.clear(); } } /** * 返回此對映所包含的值的 Collection 檢視。 * 該 collection 受對映的支援,所以對對映的更改將反映在該 collection 中, * 反之亦然。如果在對 collection 進行迭代的同時修改了對映(通過迭代器自己的 remove 操作除外), * 則迭代結果是不確定的。該 collection 支援元素的移除, * 通過 Iterator.remove、Collection.remove、removeAll、retainAll 和 clear 操作 * 可從該對映中移除相應的對映關係。它不支援 add 或 addAll 操作。 */ public Collection<V> values() { Collection<V> vs = values; return (vs != null ? vs : (values = new Values())); } // 內部類Values private final class Values extends AbstractCollection<V> { public Iterator<V> iterator() { return newValueIterator(); } public int size() { return size; } public boolean contains(Object o) { return containsValue(o); } public void clear() { HashMap.this.clear(); } } /** * 返回此對映所包含的對映關係的 Set 檢視。 * 該 set 受對映支援,所以對對映的更改將反映在此 set 中, * 反之亦然。如果在對 set 進行迭代的同時修改了對映 * (通過迭代器自己的 remove 操作,或者通過在該迭代器返回的對映項上執行 setValue 操作除外), * 則迭代結果是不確定的。該 set 支援元素的移除, * 通過 Iterator.remove、Set.remove、removeAll、retainAll 和 clear 操作 * 可從該對映中移除相應的對映關係。它不支援 add 或 addAll 操作。 */ public Set<Map.Entry<K,V>> entrySet() { return entrySet0(); } private Set<Map.Entry<K,V>> entrySet0() { Set<Map.Entry<K,V>> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } // 內部類EntrySet private final class EntrySet extends AbstractSet<Map.Entry<K,V>> { public Iterator<Map.Entry<K,V>> iterator() { return newEntryIterator(); } public boolean contains(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<K,V> e = (Map.Entry<K,V>) o; Entry<K,V> candidate = getEntry(e.getKey()); return candidate != null && candidate.equals(e); } public boolean remove(Object o) { return removeMapping(o) != null; } public int size() { return size; } public void clear() { HashMap.this.clear(); } } // 序列化方法 private void writeObject(java.io.ObjectOutputStream s) throws IOException { Iterator<Map.Entry<K,V>> i = (size > 0) ? entrySet0().iterator() : null; s.defaultWriteObject(); s.writeInt(table.length); s.writeInt(size); if (i != null) { while (i.hasNext()) { Map.Entry<K,V> e = i.next(); s.writeObject(e.getKey()); s.writeObject(e.getValue()); } } } private static final long serialVersionUID = 362498820763181265L; // 通過序列讀取物件 private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); int numBuckets = s.readInt(); table = new Entry[numBuckets]; init(); int size = s.readInt(); for (int i=0; i<size; i++) { K key = (K) s.readObject(); V value = (V) s.readObject(); putForCreate(key, value); } } int capacity() { return table.length; } float loadFactor() { return loadFactor; } }