一:HashMap簡介java
HashMap 是一個散列表,它存儲的內容是鍵值對(key-value)映射。
HashMap 繼承於AbstractMap,實現了Map、Cloneable、java.io.Serializable接口。
HashMap 的實現不是同步的,這意味着它不是線程安全的。它的key、value均可覺得null。此外,HashMap中的映射不是有序的。算法
HashMap 的實例有兩個參數影響其性能:「初始容量」 和 「加載因子」。容量 是哈希表中桶的數量,初始容量 只是哈希表在建立時的容量。加載因子 是哈希表在其容量自動增長以前能夠達到多滿的一種尺度。當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行 rehash 操做(即重建內部數據結構),從而哈希表將具備大約兩倍的桶數。
一般,默認加載因子是 0.75, 這是在時間和空間成本上尋求一種折衷。加載因子太高雖然減小了空間開銷,但同時也增長了查詢成本(在大多數 HashMap 類的操做中,包括 get 和 put 操做,都反映了這一點)。在設置初始容量時應該考慮到映射中所需的條目數及其加載因子,以便最大限度地減小 rehash 操做次數。若是初始容量大於最大條目數除以加載因子,則不會發生 rehash 操做。api
HashMap的構造函數數組
HashMap共有4個構造函數,以下:安全
// 默認構造函數。 HashMap() // 指定「容量大小」的構造函數 HashMap(int capacity) // 指定「容量大小」和「加載因子」的構造函數 HashMap(int capacity, float loadFactor) // 包含「子Map」的構造函數 HashMap(Map<? extends K, ? extends V> map)
HashMap的API數據結構
void clear() Object clone() boolean containsKey(Object key) boolean containsValue(Object value) Set<Entry<K, V>> entrySet() V get(Object key) boolean isEmpty() Set<K> keySet() V put(K key, V value) void putAll(Map<? extends K, ? extends V> map) V remove(Object key) int size() Collection<V> values()
1.ap接口概述app
查看API能夠知道:
將鍵映射到值的對象
一個映射不能包含重複的鍵
每一個鍵最多隻能映射到一個值
2.Map接口和Collection接口的不一樣
Map是雙列的,Collection是單列的
Map的鍵惟一,Collection的子體系Set是惟一的
Map集合的數據結構值針對鍵有效,跟值無關;Collection集合的數據結構是針對元素有效ide
3.HashSet底層依賴的是Map,和HashMap很類似,只是建立一個值的對象放在值的位置,可是值的位置不顯示,就是在遍歷或者使用tostring方法的時候值的位置都不顯示,都隱藏掉了。函數
爲何要這樣作,由於,這樣作的好處,他們兩個依賴的都是哈希算法,算法有一個就好,而且,鍵和值隱藏一個能夠,可是隻有一個變成兩個卻很差實現。性能
4.map中,添加元素使用put方法,把覆蓋的只返回
5.map方法綜述:
A:Map集合的功能概述
* a:添加功能
* V put(K key,V value):添加元素。
* 若是鍵是第一次存儲,就直接存儲元素,返回null
* 若是鍵不是第一次存在,就用值把之前的值替換掉,返回之前的值
* b:刪除功能
* void clear():移除全部的鍵值對元素
* V remove(Object key):根據鍵刪除鍵值對元素,並把值返回
* c:判斷功能
* boolean containsKey(Object key):判斷集合是否包含指定的鍵
* boolean containsValue(Object value):判斷集合是否包含指定的值
* boolean isEmpty():判斷集合是否爲空
* d:獲取功能
* Set<Map.Entry<K,V>> entrySet():
* V get(Object key):根據鍵獲取值
* Set<K> keySet():獲取集合中全部鍵的集合
* Collection<V> values():獲取集合中全部值的集合
* e:長度功能
* int size():返回集合中的鍵值對的個數
6.查看map集合的api發現沒有Iterator方法
Map集合的遍歷之鍵找值)
* A:鍵找值思路:
* 獲取全部鍵的集合
* 遍歷鍵的集合,獲取到每個鍵
* 根據鍵找值
* B:案例演示
* Map集合的遍歷之鍵找值
HashMap<String, Integer> hm = new HashMap<>(); hm.put("張三", 23); hm.put("李四", 24); hm.put("王五", 25); hm.put("趙六", 26);
第一種方式,經過迭代器遍歷,也能夠經過加強for循環遍歷,由於加強for循環的底層就是迭代器,能經過迭代器遍歷的都能經過加強for循環。
Set<String> keySet = hm.keySet(); //獲取集合中全部的鍵 Iterator<String> it = keySet.iterator(); //獲取迭代器 while(it.hasNext()) { //判斷單列集合中是否有元素 String key = it.next(); //獲取集合中的每個元素,其實就是雙列集合中的鍵 Integer value = hm.get(key); //根據鍵獲取值 System.out.println(key + "=" + value); //打印鍵值對 }
for(String key : hm.keySet()) { //加強for循環迭代雙列集合第一種方式 System.out.println(key + "=" + hm.get(key)); }
第二種,經過遍歷鍵值對象找鍵和值
//Map.Entry說明Entry是Map的內部接口,將鍵和值封裝成了Entry對象,並存儲在Set集合中 /*Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); //獲取每個對象 Iterator<Map.Entry<String, Integer>> it = entrySet.iterator(); while(it.hasNext()) { //獲取每個Entry對象 Map.Entry<String, Integer> en = it.next(); //父類引用指向子類對象 //Entry<String, Integer> en = it.next(); //直接獲取的是子類對象,entry是map.entry的子類對象,在map的子類hashmap中有重寫了entry String key = en.getKey(); //根據鍵值對對象獲取鍵 Integer value = en.getValue(); //根據鍵值對對象獲取值 System.out.println(key + "=" + value); }*/ for(Entry<String, Integer> en : map.entrySet()) { System.out.println(en.getKey() + "=" + en.getValue()); }
HashMap的繼承關係
java.lang.Object ↳ java.util.AbstractMap<K, V> ↳ java.util.HashMap<K, V> public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
HashMap與Map關係以下圖:
從圖中能夠看出:
(01) HashMap繼承於AbstractMap類,實現了Map接口。Map是"key-value鍵值對"接口,AbstractMap實現了"鍵值對"的通用函數接口。
(02) HashMap是經過"拉鍊法"實現的哈希表。它包括幾個重要的成員變量:table, size, threshold, loadFactor, modCount。
table是一個Entry[]數組類型,而Entry實際上就是一個單向鏈表。哈希表的"key-value鍵值對"都是存儲在Entry數組中的。
size是HashMap的大小,它是HashMap保存的鍵值對的數量。
threshold是HashMap的閾值,用於判斷是否須要調整HashMap的容量。threshold的值="容量*加載因子",當HashMap中存儲數據的數量達到threshold時,就須要將HashMap的容量加倍。
loadFactor就是加載因子。
modCount是用來實現fail-fast機制的。
package java.util; import java.io.*; public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable { // 默認的初始容量是16,必須是2的冪。 static final int DEFAULT_INITIAL_CAPACITY = 16; // 最大容量(必須是2的冪且小於2的30次方,傳入容量過大將被這個值替換) static final int MAXIMUM_CAPACITY = 1 << 30; // 默認加載因子 static final float DEFAULT_LOAD_FACTOR = 0.75f; // 存儲數據的Entry數組,長度是2的冪。 // HashMap是採用拉鍊法實現的,每個Entry本質上是一個單向鏈表 transient Entry[] table; // HashMap的大小,它是HashMap保存的鍵值對的數量 transient int size; // HashMap的閾值,用於判斷是否須要調整HashMap的容量(threshold = 容量*加載因子) int threshold; // 加載因子實際大小 final float loadFactor; // HashMap被改變的次數 transient volatile int modCount; // 指定「容量大小」和「加載因子」的構造函數 public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); // HashMap的最大容量只能是MAXIMUM_CAPACITY if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); // 找出「大於initialCapacity」的最小的2的冪 int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; // 設置「加載因子」 this.loadFactor = loadFactor; // 設置「HashMap閾值」,當HashMap中存儲數據的數量達到threshold時,就須要將HashMap的容量加倍。 threshold = (int)(capacity * loadFactor); // 建立Entry數組,用來保存數據 table = new Entry[capacity]; init(); } // 指定「容量大小」的構造函數 public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } // 默認構造函數。 public HashMap() { // 設置「加載因子」 this.loadFactor = DEFAULT_LOAD_FACTOR; // 設置「HashMap閾值」,當HashMap中存儲數據的數量達到threshold時,就須要將HashMap的容量加倍。 threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); // 建立Entry數組,用來保存數據 table = new Entry[DEFAULT_INITIAL_CAPACITY]; init(); } // 包含「子Map」的構造函數 public HashMap(Map<? extends K, ? extends V> m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); // 將m中的所有元素逐個添加到HashMap中 putAllForCreate(m); } static int hash(int h) { h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } // 返回索引值 // h & (length-1)保證返回值的小於length static int indexFor(int h, int length) { return h & (length-1); } public int size() { return size; } public boolean isEmpty() { return size == 0; } // 獲取key對應的value public V get(Object key) { if (key == null) return getForNullKey(); // 獲取key的hash值 int hash = hash(key.hashCode()); // 在「該hash值對應的鏈表」上查找「鍵值等於key」的元素 for (Entry<K,V> 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; } // 獲取「key爲null」的元素的值 // HashMap將「key爲null」的元素存儲在table[0]位置! private V getForNullKey() { for (Entry<K,V> e = table[0]; e != null; e = e.next) { if (e.key == null) return e.value; } return null; } // HashMap是否包含key public boolean containsKey(Object key) { return getEntry(key) != null; } // 返回「鍵爲key」的鍵值對 final Entry<K,V> getEntry(Object key) { // 獲取哈希值 // HashMap將「key爲null」的元素存儲在table[0]位置,「key不爲null」的則調用hash()計算哈希值 int hash = (key == null) ? 0 : hash(key.hashCode()); // 在「該hash值對應的鏈表」上查找「鍵值等於key」的元素 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null; } // 將「key-value」添加到HashMap中 public V put(K key, V value) { // 若「key爲null」,則將該鍵值對添加到table[0]中。 if (key == null) return putForNullKey(value); // 若「key不爲null」,則計算該key的哈希值,而後將其添加到該哈希值對應的鏈表中。 int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; // 若「該key」對應的鍵值對已經存在,則用新的value取代舊的value。而後退出! if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } // 若「該key」對應的鍵值對不存在,則將「key-value」添加到table中 modCount++; addEntry(hash, key, value, i); return null; } // putForNullKey()的做用是將「key爲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對應的「添加方法」, // 它和put()不一樣。putForCreate()是內部方法,它被構造函數等調用,用來建立HashMap // 而put()是對外提供的往HashMap中添加元素的方法。 private void putForCreate(K key, V value) { int hash = (key == null) ? 0 : hash(key.hashCode()); int i = indexFor(hash, table.length); // 若該HashMap表中存在「鍵值等於key」的元素,則替換該元素的value值 for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { e.value = value; return; } } // 若該HashMap表中不存在「鍵值等於key」的元素,則將該key-value添加到HashMap中 createEntry(hash, key, value, i); } // 將「m」中的所有元素都添加到HashMap中。 // 該方法被內部的構造HashMap的方法所調用。 private void putAllForCreate(Map<? extends K, ? extends V> m) { // 利用迭代器將元素逐個添加到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(); putForCreate(e.getKey(), e.getValue()); } } // 從新調整HashMap的大小,newCapacity是調整後的單位 void resize(int newCapacity) { Entry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } // 新建一個HashMap,將「舊HashMap」的所有元素添加到「新HashMap」中, // 而後,將「新HashMap」賦值給「舊HashMap」。 Entry[] newTable = new Entry[newCapacity]; transfer(newTable); table = newTable; threshold = (int)(newCapacity * loadFactor); } // 將HashMap中的所有元素都添加到newTable中 void transfer(Entry[] newTable) { Entry[] src = table; int newCapacity = newTable.length; for (int j = 0; j < src.length; j++) { Entry<K,V> e = src[j]; if (e != null) { src[j] = null; do { Entry<K,V> next = e.next; int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } while (e != null); } } } // 將"m"的所有元素都添加到HashMap中 public void putAll(Map<? extends K, ? extends V> m) { // 有效性判斷 int numKeysToBeAdded = m.size(); if (numKeysToBeAdded == 0) return; // 計算容量是否足夠, // 若「當前實際容量 < 須要的容量」,則將容量x2。 if (numKeysToBeAdded > threshold) { 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); } // 經過迭代器,將「m」中的元素逐個添加到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()); } } // 刪除「鍵爲key」元素 public V remove(Object key) { Entry<K,V> e = removeEntryForKey(key); return (e == null ? null : e.value); } // 刪除「鍵爲key」的元素 final Entry<K,V> removeEntryForKey(Object key) { // 獲取哈希值。若key爲null,則哈希值爲0;不然調用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; // 刪除鏈表中「鍵爲key」的元素 // 本質是「刪除單向鏈表中的節點」 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--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; } return e; } // 刪除「鍵值對」 final Entry<K,V> removeMapping(Object o) { if (!(o instanceof Map.Entry)) return null; Map.Entry<K,V> entry = (Map.Entry<K,V>) o; Object key = entry.getKey(); 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; // 刪除鏈表中的「鍵值對e」 // 本質是「刪除單向鏈表中的節點」 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; } // 清空HashMap,將全部的元素設爲null public void clear() { modCount++; Entry[] tab = table; for (int i = 0; i < tab.length; i++) tab[i] = null; size = 0; } // 是否包含「值爲value」的元素 public boolean containsValue(Object value) { // 若「value爲null」,則調用containsNullValue()查找 if (value == null) return containsNullValue(); // 若「value不爲null」,則查找HashMap中是否有值爲value的節點。 Entry[] tab = table; for (int i = 0; i < tab.length ; i++) for (Entry e = tab[i] ; e != null ; e = e.next) if (value.equals(e.value)) return true; return false; } // 是否包含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,並返回Object對象 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(); // 調用putAllForCreate()將所有元素添加到HashMap中 result.putAllForCreate(this); return result; } // Entry是單向鏈表。 // 它是 「HashMap鏈式存儲法」對應的鏈表。 // 它實現了Map.Entry 接口,即實現getKey(), getValue(), setValue(V value), equals(Object o), hashCode()這些函數 static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; // 指向下一個節點 Entry<K,V> next; final int hash; // 構造函數。 // 輸入參數包括"哈希值(h)", "鍵(k)", "值(v)", "下一節點(n)" Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; } public final K getKey() { return key; } public final V getValue() { return value; } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } // 判斷兩個Entry是否相等 // 若兩個Entry的「key」和「value」都相等,則返回true。 // 不然,返回false public final boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; 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; } return false; } // 實現hashCode() public final int hashCode() { return (key==null ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode()); } public final String toString() { return getKey() + "=" + getValue(); } // 當向HashMap中添加元素時,繪調用recordAccess()。 // 這裏不作任何處理 void recordAccess(HashMap<K,V> m) { } // 當從HashMap中刪除元素時,繪調用recordRemoval()。 // 這裏不作任何處理 void recordRemoval(HashMap<K,V> m) { } } // 新增Entry。將「key-value」插入指定位置,bucketIndex是位置索引。 void addEntry(int hash, K key, V value, int bucketIndex) { // 保存「bucketIndex」位置的值到「e」中 Entry<K,V> e = table[bucketIndex]; // 設置「bucketIndex」位置的元素爲「新Entry」, // 設置「e」爲「新Entry的下一個節點」 table[bucketIndex] = new Entry<K,V>(hash, key, value, e); // 若HashMap的實際大小 不小於 「閾值」,則調整HashMap的大小 if (size++ >= threshold) resize(2 * table.length); } // 建立Entry。將「key-value」插入指定位置,bucketIndex是位置索引。 // 它和addEntry的區別是: // (01) addEntry()通常用在 新增Entry可能致使「HashMap的實際容量」超過「閾值」的狀況下。 // 例如,咱們新建一個HashMap,而後不斷經過put()向HashMap中添加元素; // put()是經過addEntry()新增Entry的。 // 在這種狀況下,咱們不知道什麼時候「HashMap的實際容量」會超過「閾值」; // 所以,須要調用addEntry() // (02) createEntry() 通常用在 新增Entry不會致使「HashMap的實際容量」超過「閾值」的狀況下。 // 例如,咱們調用HashMap「帶有Map」的構造函數,它繪將Map的所有元素添加到HashMap中; // 但在添加以前,咱們已經計算好「HashMap的容量和閾值」。也就是,能夠肯定「即便將Map中 // 的所有元素添加到HashMap中,都不會超過HashMap的閾值」。 // 此時,調用createEntry()便可。 void createEntry(int hash, K key, V value, int bucketIndex) { // 保存「bucketIndex」位置的值到「e」中 Entry<K,V> e = table[bucketIndex]; // 設置「bucketIndex」位置的元素爲「新Entry」, // 設置「e」爲「新Entry的下一個節點」 table[bucketIndex] = new Entry<K,V>(hash, key, value, e); size++; } // HashIterator是HashMap迭代器的抽象出來的父類,實現了公共了函數。 // 它包含「key迭代器(KeyIterator)」、「Value迭代器(ValueIterator)」和「Entry迭代器(EntryIterator)」3個子類。 private abstract class HashIterator<E> implements Iterator<E> { // 下一個元素 Entry<K,V> next; // expectedModCount用於實現fast-fail機制。 int expectedModCount; // 當前索引 int index; // 當前元素 Entry<K,V> current; HashIterator() { expectedModCount = modCount; if (size > 0) { // advance to first entry Entry[] t = table; // 將next指向table中第一個不爲null的元素。 // 這裏利用了index的初始值爲0,從0開始依次向後遍歷,直到找到不爲null的元素就退出循環。 while (index < t.length && (next = t[index++]) == null) ; } } public final boolean hasNext() { return next != null; } // 獲取下一個元素 final Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); Entry<K,V> e = next; if (e == null) throw new NoSuchElementException(); // 注意!!! // 一個Entry就是一個單向鏈表 // 若該Entry的下一個節點不爲空,就將next指向下一個節點; // 不然,將next指向下一個鏈表(也是下一個Entry)的不爲null的節點。 if ((next = e.next) == null) { Entry[] t = table; while (index < t.length && (next = t[index++]) == null) ; } current = e; return e; } // 刪除當前元素 public void remove() { if (current == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); Object k = current.key; current = null; HashMap.this.removeEntryForKey(k); expectedModCount = modCount; } } // value的迭代器 private final class ValueIterator extends HashIterator<V> { public V next() { return nextEntry().value; } } // key的迭代器 private final class KeyIterator extends HashIterator<K> { public K next() { return nextEntry().getKey(); } } // Entry的迭代器 private final class EntryIterator extends HashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } } // 返回一個「key迭代器」 Iterator<K> newKeyIterator() { return new KeyIterator(); } // 返回一個「value迭代器」 Iterator<V> newValueIterator() { return new ValueIterator(); } // 返回一個「entry迭代器」 Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); } // HashMap的Entry對應的集合 private transient Set<Map.Entry<K,V>> entrySet = null; // 返回「key的集合」,實際上返回一個「KeySet對象」 public Set<K> keySet() { Set<K> ks = keySet; return (ks != null ? ks : (keySet = new KeySet())); } // Key對應的集合 // KeySet繼承於AbstractSet,說明該集合中沒有重複的Key。 private final class KeySet extends AbstractSet<K> { public Iterator<K> iterator() { return newKeyIterator(); } public int size() { return size; } public boolean contains(Object o) { return containsKey(o); } public boolean remove(Object o) { return HashMap.this.removeEntryForKey(o) != null; } public void clear() { HashMap.this.clear(); } } // 返回「value集合」,實際上返回的是一個Values對象 public Collection<V> values() { Collection<V> vs = values; return (vs != null ? vs : (values = new Values())); } // 「value集合」 // Values繼承於AbstractCollection,不一樣於「KeySet繼承於AbstractSet」, // Values中的元素可以重複。由於不一樣的key能夠指向相同的value。 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(); } } // 返回「HashMap的Entry集合」 public Set<Map.Entry<K,V>> entrySet() { return entrySet0(); } // 返回「HashMap的Entry集合」,它實際是返回一個EntrySet對象 private Set<Map.Entry<K,V>> entrySet0() { Set<Map.Entry<K,V>> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } // EntrySet對應的集合 // EntrySet繼承於AbstractSet,說明該集合中沒有重複的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(); } } // java.io.Serializable的寫入函數 // 將HashMap的「總的容量,實際容量,全部的Entry」都寫入到輸出流中 private void writeObject(java.io.ObjectOutputStream s) throws IOException { Iterator<Map.Entry<K,V>> i = (size > 0) ? entrySet0().iterator() : null; // Write out the threshold, loadfactor, and any hidden stuff s.defaultWriteObject(); // Write out number of buckets s.writeInt(table.length); // Write out size (number of Mappings) s.writeInt(size); // Write out keys and values (alternating) 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; // java.io.Serializable的讀取函數:根據寫入方式讀出 // 將HashMap的「總的容量,實際容量,全部的Entry」依次讀出 private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the threshold, loadfactor, and any hidden stuff s.defaultReadObject(); // Read in number of buckets and allocate the bucket array; int numBuckets = s.readInt(); table = new Entry[numBuckets]; init(); // Give subclass a chance to do its thing. // Read in size (number of Mappings) int size = s.readInt(); // Read the keys and values, and put the mappings in the HashMap for (int i=0; i<size; i++) { K key = (K) s.readObject(); V value = (V) s.readObject(); putForCreate(key, value); } } // 返回「HashMap總的容量」 int capacity() { return table.length; } // 返回「HashMap的加載因子」 float loadFactor() { return loadFactor; } }
說明:
在詳細介紹HashMap的代碼以前,咱們須要瞭解:HashMap就是一個散列表,它是經過「拉鍊法」解決哈希衝突的。
還須要再補充說明的一點是影響HashMap性能的有兩個參數:初始容量(initialCapacity) 和加載因子(loadFactor)。容量 是哈希表中桶的數量,初始容量只是哈希表在建立時的容量。加載因子 是哈希表在其容量自動增長以前能夠達到多滿的一種尺度。當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行 rehash 操做(即重建內部數據結構),從而哈希表將具備大約兩倍的桶數。
3.1.1 HashMap數據存儲數組
transient Entry[] table;
HashMap中的key-value都是存儲在Entry數組中的。
3.1.2 數據節點Entry的數據結構
static class Entry<K,V> implements Map.Entry<K,V> { final K key; V value; // 指向下一個節點 Entry<K,V> next; final int hash; // 構造函數。 // 輸入參數包括"哈希值(h)", "鍵(k)", "值(v)", "下一節點(n)" Entry(int h, K k, V v, Entry<K,V> n) { value = v; next = n; key = k; hash = h; } public final K getKey() { return key; } public final V getValue() { return value; } public final V setValue(V newValue) { V oldValue = value; value = newValue; return oldValue; } // 判斷兩個Entry是否相等 // 若兩個Entry的「key」和「value」都相等,則返回true。 // 不然,返回false public final boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry e = (Map.Entry)o; 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; } return false; } // 實現hashCode() public final int hashCode() { return (key==null ? 0 : key.hashCode()) ^ (value==null ? 0 : value.hashCode()); } public final String toString() { return getKey() + "=" + getValue(); } // 當向HashMap中添加元素時,繪調用recordAccess()。 // 這裏不作任何處理 void recordAccess(HashMap<K,V> m) { } // 當從HashMap中刪除元素時,繪調用recordRemoval()。 // 這裏不作任何處理 void recordRemoval(HashMap<K,V> m) { } }
從中,咱們能夠看出 Entry 實際上就是一個單向鏈表。這也是爲何咱們說HashMap是經過拉鍊法解決哈希衝突的。
Entry 實現了Map.Entry 接口,即實現getKey(), getValue(), setValue(V value), equals(Object o), hashCode()這些函數。這些都是基本的讀取/修改key、value值的函數。
HashMap共包括4個構造函數
// 默認構造函數。 public HashMap() { // 設置「加載因子」 this.loadFactor = DEFAULT_LOAD_FACTOR; // 設置「HashMap閾值」,當HashMap中存儲數據的數量達到threshold時,就須要將HashMap的容量加倍。 threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); // 建立Entry數組,用來保存數據 table = new Entry[DEFAULT_INITIAL_CAPACITY]; init(); } // 指定「容量大小」和「加載因子」的構造函數 public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); // HashMap的最大容量只能是MAXIMUM_CAPACITY if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); // Find a power of 2 >= initialCapacity int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; // 設置「加載因子」 this.loadFactor = loadFactor; // 設置「HashMap閾值」,當HashMap中存儲數據的數量達到threshold時,就須要將HashMap的容量加倍。 threshold = (int)(capacity * loadFactor); // 建立Entry數組,用來保存數據 table = new Entry[capacity]; init(); } // 指定「容量大小」的構造函數 public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } // 包含「子Map」的構造函數 public HashMap(Map<? extends K, ? extends V> m) { this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR); // 將m中的所有元素逐個添加到HashMap中 putAllForCreate(m); }
3.3.1 clear()
clear() 的做用是清空HashMap。它是經過將全部的元素設爲null來實現的
public void clear() { modCount++; Entry[] tab = table; for (int i = 0; i < tab.length; i++) tab[i] = null; size = 0; }
3.3.2 containsKey()
containsKey() 的做用是判斷HashMap是否包含key。
public boolean containsKey(Object key) { return getEntry(key) != null; }
containsKey() 首先經過getEntry(key)獲取key對應的Entry,而後判斷該Entry是否爲null。
getEntry()的源碼以下:
final Entry<K,V> getEntry(Object key) { // 獲取哈希值 // HashMap將「key爲null」的元素存儲在table[0]位置,「key不爲null」的則調用hash()計算哈希值 int hash = (key == null) ? 0 : hash(key.hashCode()); // 在「該hash值對應的鏈表」上查找「鍵值等於key」的元素 for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } return null; }
getEntry() 的做用就是返回「鍵爲key」的鍵值對,它的實現源碼中已經進行了說明。
這裏須要強調的是:HashMap將「key爲null」的元素都放在table的位置0處,即table[0]中;「key不爲null」的放在table的其他位置!
3.3.3 containsValue()
containsValue() 的做用是判斷HashMap是否包含「值爲value」的元素。
public boolean containsValue(Object value) { // 若「value爲null」,則調用containsNullValue()查找 if (value == null) return containsNullValue(); // 若「value不爲null」,則查找HashMap中是否有值爲value的節點。 Entry[] tab = table; for (int i = 0; i < tab.length ; i++) for (Entry e = tab[i] ; e != null ; e = e.next) if (value.equals(e.value)) return true; return false; }
從中,咱們能夠看出containsNullValue()分爲兩步進行處理:第一,若「value爲null」,則調用containsNullValue()。第二,若「value不爲null」,則查找HashMap中是否有值爲value的節點。
containsNullValue() 的做用判斷HashMap中是否包含「值爲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; }
3.3.4 entrySet()、values()、keySet()
它們3個的原理相似,這裏以entrySet()爲例來講明。
entrySet()的做用是返回「HashMap中全部Entry的集合」,它是一個集合。實現代碼以下:
// 返回「HashMap的Entry集合」 public Set<Map.Entry<K,V>> entrySet() { return entrySet0(); } // 返回「HashMap的Entry集合」,它實際是返回一個EntrySet對象 private Set<Map.Entry<K,V>> entrySet0() { Set<Map.Entry<K,V>> es = entrySet; return es != null ? es : (entrySet = new EntrySet()); } // EntrySet對應的集合 // EntrySet繼承於AbstractSet,說明該集合中沒有重複的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(); } }
HashMap是經過拉鍊法實現的散列表。表如今HashMap包括許多的Entry,而每個Entry本質上又是一個單向鏈表。那麼HashMap遍歷key-value鍵值對的時候,是如何逐個去遍歷的呢?
下面咱們就看看HashMap是如何經過entrySet()遍歷的。
entrySet()其實是經過newEntryIterator()實現的。 下面咱們看看它的代碼:
// 返回一個「entry迭代器」 Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); } // Entry的迭代器 private final class EntryIterator extends HashIterator<Map.Entry<K,V>> { public Map.Entry<K,V> next() { return nextEntry(); } } // HashIterator是HashMap迭代器的抽象出來的父類,實現了公共了函數。 // 它包含「key迭代器(KeyIterator)」、「Value迭代器(ValueIterator)」和「Entry迭代器(EntryIterator)」3個子類。 private abstract class HashIterator<E> implements Iterator<E> { // 下一個元素 Entry<K,V> next; // expectedModCount用於實現fast-fail機制。 int expectedModCount; // 當前索引 int index; // 當前元素 Entry<K,V> current; HashIterator() { expectedModCount = modCount; if (size > 0) { // advance to first entry Entry[] t = table; // 將next指向table中第一個不爲null的元素。 // 這裏利用了index的初始值爲0,從0開始依次向後遍歷,直到找到不爲null的元素就退出循環。 while (index < t.length && (next = t[index++]) == null) ; } } public final boolean hasNext() { return next != null; } // 獲取下一個元素 final Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); Entry<K,V> e = next; if (e == null) throw new NoSuchElementException(); // 注意!!! // 一個Entry就是一個單向鏈表 // 若該Entry的下一個節點不爲空,就將next指向下一個節點; // 不然,將next指向下一個鏈表(也是下一個Entry)的不爲null的節點。 if ((next = e.next) == null) { Entry[] t = table; while (index < t.length && (next = t[index++]) == null) ; } current = e; return e; } // 刪除當前元素 public void remove() { if (current == null) throw new IllegalStateException(); if (modCount != expectedModCount) throw new ConcurrentModificationException(); Object k = current.key; current = null; HashMap.this.removeEntryForKey(k); expectedModCount = modCount; } }
當咱們經過entrySet()獲取到的Iterator的next()方法去遍歷HashMap時,實際上調用的是 nextEntry() 。而nextEntry()的實現方式,先遍歷Entry(根據Entry在table中的序號,從小到大的遍歷);而後對每一個Entry(即每一個單向鏈表),逐個遍歷。
3.3.5 get()
get() 的做用是獲取key對應的value,它的實現代碼以下:
public V get(Object key) { if (key == null) return getForNullKey(); // 獲取key的hash值 int hash = hash(key.hashCode()); // 在「該hash值對應的鏈表」上查找「鍵值等於key」的元素 for (Entry<K,V> 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; }
3.3.6 put()
put() 的做用是對外提供接口,讓HashMap對象能夠經過put()將「key-value」添加到HashMap中。
public V put(K key, V value) { // 若「key爲null」,則將該鍵值對添加到table[0]中。 if (key == null) return putForNullKey(value); // 若「key不爲null」,則計算該key的哈希值,而後將其添加到該哈希值對應的鏈表中。 int hash = hash(key.hashCode()); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { Object k; // 若「該key」對應的鍵值對已經存在,則用新的value取代舊的value。而後退出! if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } // 若「該key」對應的鍵值對不存在,則將「key-value」添加到table中 modCount++; addEntry(hash, key, value, i); return null; }
若要添加到HashMap中的鍵值對對應的key已經存在HashMap中,則找到該鍵值對;而後新的value取代舊的value,並退出!
若要添加到HashMap中的鍵值對對應的key不在HashMap中,則將其添加到該哈希值對應的鏈表中,並調用addEntry()。
下面看看addEntry()的代碼:
void addEntry(int hash, K key, V value, int bucketIndex) { // 保存「bucketIndex」位置的值到「e」中 Entry<K,V> e = table[bucketIndex]; // 設置「bucketIndex」位置的元素爲「新Entry」, // 設置「e」爲「新Entry的下一個節點」 table[bucketIndex] = new Entry<K,V>(hash, key, value, e); // 若HashMap的實際大小 不小於 「閾值」,則調整HashMap的大小 if (size++ >= threshold) resize(2 * table.length); }
addEntry() 的做用是新增Entry。將「key-value」插入指定位置,bucketIndex是位置索引。
說到addEntry(),就不得不說另外一個函數createEntry()。createEntry()的代碼以下:
void createEntry(int hash, K key, V value, int bucketIndex) { // 保存「bucketIndex」位置的值到「e」中 Entry<K,V> e = table[bucketIndex]; // 設置「bucketIndex」位置的元素爲「新Entry」, // 設置「e」爲「新Entry的下一個節點」 table[bucketIndex] = new Entry<K,V>(hash, key, value, e); size++; }
它們的做用都是將key、value添加到HashMap中。並且,比較addEntry()和createEntry()的代碼,咱們發現addEntry()多了兩句:
if (size++ >= threshold) resize(2 * table.length);
那它們的區別究竟是什麼呢?
閱讀代碼,咱們能夠發現,它們的使用情景不一樣。
(01) addEntry()通常用在 新增Entry可能致使「HashMap的實際容量」超過「閾值」的狀況下。
例如,咱們新建一個HashMap,而後不斷經過put()向HashMap中添加元素;put()是經過addEntry()新增Entry的。
在這種狀況下,咱們不知道什麼時候「HashMap的實際容量」會超過「閾值」;
所以,須要調用addEntry()
(02) createEntry() 通常用在 新增Entry不會致使「HashMap的實際容量」超過「閾值」的狀況下。
例如,咱們調用HashMap「帶有Map」的構造函數,它繪將Map的所有元素添加到HashMap中;
但在添加以前,咱們已經計算好「HashMap的容量和閾值」。也就是,能夠肯定「即便將Map中的所有元素添加到HashMap中,都不會超過HashMap的閾值」。
此時,調用createEntry()便可。
3.3.7 putAll()
putAll() 的做用是將"m"的所有元素都添加到HashMap中,它的代碼以下:
public void putAll(Map<? extends K, ? extends V> m) { // 有效性判斷 int numKeysToBeAdded = m.size(); if (numKeysToBeAdded == 0) return; // 計算容量是否足夠, // 若「當前實際容量 < 須要的容量」,則將容量x2。 if (numKeysToBeAdded > threshold) { 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); } // 經過迭代器,將「m」中的元素逐個添加到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()); } }
3.3.8 remove()
remove() 的做用是刪除「鍵爲key」元素
public V remove(Object key) { Entry<K,V> e = removeEntryForKey(key); return (e == null ? null : e.value); } // 刪除「鍵爲key」的元素 final Entry<K,V> removeEntryForKey(Object key) { // 獲取哈希值。若key爲null,則哈希值爲0;不然調用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; // 刪除鏈表中「鍵爲key」的元素 // 本質是「刪除單向鏈表中的節點」 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--; if (prev == e) table[i] = next; else prev.next = next; e.recordRemoval(this); return e; } prev = e; e = next; } return e; }
HashMap實現了Cloneable接口,即實現了clone()方法。
clone()方法的做用很簡單,就是克隆一個HashMap對象並返回。
// 克隆一個HashMap,並返回Object對象 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(); // 調用putAllForCreate()將所有元素添加到HashMap中 result.putAllForCreate(this); return result; }
第3.5部分 HashMap實現的Serializable接口
HashMap實現java.io.Serializable,分別實現了串行讀取、寫入功能。
串行寫入函數是writeObject(),它的做用是將HashMap的「總的容量,實際容量,全部的Entry」都寫入到輸出流中。
而串行讀取函數是readObject(),它的做用是將HashMap的「總的容量,實際容量,全部的Entry」依次讀出
// java.io.Serializable的寫入函數 // 將HashMap的「總的容量,實際容量,全部的Entry」都寫入到輸出流中 private void writeObject(java.io.ObjectOutputStream s) throws IOException { Iterator<Map.Entry<K,V>> i = (size > 0) ? entrySet0().iterator() : null; // Write out the threshold, loadfactor, and any hidden stuff s.defaultWriteObject(); // Write out number of buckets s.writeInt(table.length); // Write out size (number of Mappings) s.writeInt(size); // Write out keys and values (alternating) if (i != null) { while (i.hasNext()) { Map.Entry<K,V> e = i.next(); s.writeObject(e.getKey()); s.writeObject(e.getValue()); } } } // java.io.Serializable的讀取函數:根據寫入方式讀出 // 將HashMap的「總的容量,實際容量,全部的Entry」依次讀出 private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { // Read in the threshold, loadfactor, and any hidden stuff s.defaultReadObject(); // Read in number of buckets and allocate the bucket array; int numBuckets = s.readInt(); table = new Entry[numBuckets]; init(); // Give subclass a chance to do its thing. // Read in size (number of Mappings) int size = s.readInt(); // Read the keys and values, and put the mappings in the HashMap for (int i=0; i<size; i++) { K key = (K) s.readObject(); V value = (V) s.readObject(); putForCreate(key, value); } }
Entry