public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
基於hash表的Map集合,容許key=null和value=null,HashMap是不一樣步的,不能保證Map集合的順序,它是無序的Map集合.
HashMap有兩個參數影響它的性能: 初始化容量(initial capacity) 和 負載因子(load factor)。
初始化容量:就是建立 hash表時的容量
負載因子:負載因子是衡量在哈希表的容量被自動增長以前,哈希表被容許得到多少滿的度量。
當哈希表中的條目數超過負載因子和當前容量的乘積時,哈希表將被從新哈希(即從新構建內部數據結構),
這樣哈希表的桶數大約是桶數的兩倍。
默認的負載因子是0.75f,這是一個在時間和空間上的一個折中;較高的值減小了空間開銷,但增長了查找成本(主要表如今HaspMap的get和put操做)。
若是初始容量大於最大條目數除以負載因子,則不會發生任何重哈希操做。
底層數據結構是鏈表數組,
1 /** 2 * 默認初始化容量是16 3 */ 4 static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 5 6 /** 7 * 最大的容量,若是較高的值由帶參數的任何構造函數隱式指定,則使用。 8 * 必須是2的冪,最大容量爲1073741824 9 */ 10 static final int MAXIMUM_CAPACITY = 1 << 30;//1073741824 11 12 /** 13 * 當構造函數中沒有指定時使用的負載因子,默認是0.75f; 14 */ 15 static final float DEFAULT_LOAD_FACTOR = 0.75f; 16 17 // 一個桶的樹化閾值 18 // 當桶中元素個數超過這個值時,須要使用紅黑樹節點替換鏈表節點 19 // 這個值必須爲 8,要否則頻繁轉換效率也不高 20 static final int TREEIFY_THRESHOLD = 8; 21 22 // 一個樹的鏈表還原閾值 23 // 當擴容時,桶中元素個數小於這個值,就會把樹形的桶元素 還原(切分)爲鏈表結構 24 // 這個值應該比上面那個小,至少爲 6,避免頻繁轉換 25 static final int UNTREEIFY_THRESHOLD = 6; 26 27 28 // 哈希表的最小樹形化容量 29 // 當哈希表中的容量大於這個值時,表中的桶才能進行樹形化 30 // 不然桶內元素太多時會擴容,而不是樹形化 31 // 爲了不進行擴容、樹形化選擇的衝突,這個值不能小於 4 * TREEIFY_THRESHOLD 32 static final int MIN_TREEIFY_CAPACITY = 64;
基本的hash表節點:node
1 /** 2 * 基本的hash表節點, 3 * (參見下面的forTreeNode子類,以及LinkedHashMap中的條目子類。) 4 */ 5 static class Node<K,V> implements Map.Entry<K,V> { 6 final int hash;//hash值 final修飾的 7 final K key;//鍵 final修飾的 8 V value;//值 9 Node<K,V> next;//後置節點 10 11 //構造函數 12 Node(int hash, K key, V value, Node<K,V> next) { 13 this.hash = hash; 14 this.key = key; 15 this.value = value; 16 this.next = next; 17 } 18 19 public final K getKey() { return key; } 20 public final V getValue() { return value; } 21 public final String toString() { return key + "=" + value; } 22 23 public final int hashCode() { 24 return Objects.hashCode(key) ^ Objects.hashCode(value); 25 } 26 27 public final V setValue(V newValue) { 28 V oldValue = value; 29 value = newValue; 30 return oldValue; 31 } 32 33 public final boolean equals(Object o) { 34 if (o == this) 35 return true; 36 if (o instanceof Map.Entry) { 37 Map.Entry<?,?> e = (Map.Entry<?,?>)o; 38 if (Objects.equals(key, e.getKey()) && 39 Objects.equals(value, e.getValue())) 40 return true; 41 } 42 return false; 43 } 44 }
1 /** 2 * The table, initialized on first use, and resized as 3 * necessary. When allocated, length is always a power of two. 4 * (We also tolerate length zero in some operations to allow 5 * bootstrapping mechanics that are currently not needed.) 6 * 第一次初始化的時候使用這個表。當分配時,長度老是2的冪 7 */ 8 transient Node<K,V>[] table; 9 10 /** 11 * Holds cached entrySet(). Note that AbstractMap fields are used for keySet() and values(). 12 * 保存緩存entrySet()。注意AbstractMap字段用於keySet()和values()。 13 */ 14 transient Set<Map.Entry<K,V>> entrySet; 15 16 /** 17 * The number of key-value mappings contained in this map. 18 * 此映射中包含的鍵值映射的數目。 19 */ 20 transient int size; 21 22 /** 23 * 被修改的次數 24 */ 25 transient int modCount; 26 27 28 29 /** 30 * The next size value at which to resize (capacity * load factor). 31 * 要調整大小的下一個大小值(容量*負載因子)。 32 * @serial 33 */ 34 int threshold;//要調整大小的下一個大小值(容量*負載因子)。 35 36 /** 37 * 哈希表的負載因子。 38 * @serial 39 */ 40 final float loadFactor;
構造函數:bootstrap
1 /** 2 * 構造一個空的HashMap,使用專門的初始化容量和默認的負載因子。 3 * @param initialCapacity the initial capacity. 4 * @throws IllegalArgumentException if the initial capacity is negative. 5 */ 6 public HashMap(int initialCapacity) { 7 this(initialCapacity, DEFAULT_LOAD_FACTOR); 8 } 9 10 /** 11 * (16) and the default load factor (0.75). 12 * 構造一個空的HashMap,使用默認的初始化容量(16)和默認的負載因子(0.75f)。 13 */ 14 public HashMap() { 15 this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted 16 } 17 18 /** 19 * 構造一個新的HashMap,使用與指定映射相同的映射。裝載因子使用默認的(0.75f),和足夠容納指定Map中的映射的初始容量。 20 * @param m the map whose mappings are to be placed in this map 要在此映射中放置其映射的映射的映射 21 * @throws NullPointerException if the specified map is null 若是這個指定的map爲null,則拋出空指針異常NullPointerException 22 */ 23 public HashMap(Map<? extends K, ? extends V> m) { 24 this.loadFactor = DEFAULT_LOAD_FACTOR; 25 putMapEntries(m, false); 26 }
1 /** 2 * 實現 Map.putAll和 Map的構造函數 3 * @param m the map 4 * @param evict 當最初構造這個map的時候爲false,不然爲true(傳遞到以後的插入節點的方法中)。 5 */ 6 final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) { 7 int s = m.size(); 8 if (s > 0) { 9 if (table == null) { // pre-size 10 float ft = ((float)s / loadFactor) + 1.0F; 11 int t = ((ft < (float)MAXIMUM_CAPACITY) ? (int)ft : MAXIMUM_CAPACITY); 12 if (t > threshold) { 13 threshold = tableSizeFor(t); 14 } 15 }else if (s > threshold) { 16 resize(); 17 } 18 for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) { 19 K key = e.getKey(); 20 V value = e.getValue(); 21 putVal(hash(key), key, value, false, evict); 22 } 23 } 24 }
1 /** 2 * @return 返回此映射中鍵值映射的數目。 3 */ 4 public int size() { 5 return size; 6 } 7 8 /** 9 * 判斷該map是否爲空 10 * @return 當此map中不含鍵值對的時候,返回true. 11 */ 12 public boolean isEmpty() { 13 return size == 0; 14 }
get方法(先比較hash,若相等在比較equals):數組
1. Key==null的時候,判斷map也爲空,就返回null
2. 根據node數組下標找到node數組下標
3. 若是當前node鏈表只存在一個數據就直接取value值
若是當前node鏈表存在多個node元素,則循環遍歷node鏈表,分別對他們的hash值和key值,value值進行判斷,知道找到node之後返回Value值,若是沒有找到返回null。緩存
1 /** 2 * 根據鍵key獲取值value,若是此map不包含這個key,則返回null。 3 * Returns the value to which the specified key is mapped, 4 * or {@code null} if this map contains no mapping for the key. 5 * 6 * <p>More formally, if this map contains a mapping from a key 7 * {@code k} to a value {@code v} such that {@code (key==null ? k==null : 8 * key.equals(k))}, then this method returns {@code v}; otherwise 9 * it returns {@code null}. (There can be at most one such mapping.) 10 * 11 * <p>A return value of {@code null} does not <i>necessarily</i> 12 * indicate that the map contains no mapping for the key; it's also 13 * possible that the map explicitly maps the key to {@code null}. 14 * The {@link #containsKey containsKey} operation may be used to 15 * distinguish these two cases. 16 * 17 * @see #put(Object, Object) 18 */ 19 public V get(Object key) { 20 Node<K,V> e; 21 return (e = getNode(hash(key), key)) == null ? null : e.value; 22 } 23 24 /** 25 * Implements Map.get and related methods 26 * Map.get相關方法的實現 27 * @param hash hash for key 鍵key的hash值 28 * @param key the key 鍵key 29 * @return the node, or null if none 返回根據key及key的hash找到的這個節點;若是這個節點爲空,則返回null 30 * 31 * 32 */ 33 final Node<K,V> getNode(int hash, Object key) { 34 Node<K,V>[] tab; 35 Node<K,V> first, e; 36 int n; 37 K k; 38 if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { 39 // always check first node 老是會檢查哈希表的第一個節點 40 if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) { 41 return first; 42 } 43 if ((e = first.next) != null) {//若是第一個節點的下一個節點不爲空 44 if (first instanceof TreeNode) {//若是該節點是TreeNode類型的(紅黑樹) 45 return ((TreeNode<K, V>) first).getTreeNode(hash, key);//調用紅黑樹的查找方法 46 } 47 do { 48 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { 49 return e; 50 } 51 } while ((e = e.next) != null); 52 } 53 } 54 return null; 55 }
put方法:數據結構
1. 首先判斷node數組,也就是數組是否爲空,爲空的話就初始化hashmap
2. 若是node數組不爲空的話,就判斷key是否爲空,爲空的話就將放到數組的第一個位置
3. 就經過key獲取hash值,經過hash值作比較,若是key的hash值相等 而且key.equals(e.key)也相等的話,就將新的value替換掉舊的。若是條件不知足就建立一個node,且用上一個node的next指向新建立的node 。app
1 /** 2 * 往map中添加新的鍵值對,若是鍵key存在,則將該鍵key對應的舊值value替換爲新值value 3 * 4 * @param key 要與指定值關聯的鍵 5 * @param value 值與指定的鍵關聯 6 * @return 與key關聯的前一個值,若是沒有key的映射,則爲null。(null返回值還能夠指示之前將null與key關聯的映射。) 7 */ 8 public V put(K key, V value) { 9 return putVal(hash(key), key, value, false, true); 10 } 11 12 /** 13 * Implements Map.put and related methods 14 * 實現Map.put和相關方法 15 * @param hash hash for key key的hash值 16 * @param key the key 鍵 17 * @param value the value to put 值 18 * @param onlyIfAbsent 若是是true,不能改變已存在的值 19 * @param evict 若是爲false,該表處於建立模式 20 * @return 返回前一個值,若是沒有,則爲空 21 * 根據key算hash,根據容量和hash算index,table[index]沒有直接添加到數組中,table[index]有,若index位置同一個key則更新, 22 * 不然遍歷next是否有,有則更新,無則新增,最後根據thread與size判斷是否擴容。注:擴容時容量翻倍,從新算hash複製到新數組 23 * 24 */ 25 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, 26 boolean evict) { 27 Node<K,V>[] tab; 28 Node<K,V> p; 29 int n, i; 30 if ((tab = table) == null || (n = tab.length) == 0) { 31 n = (tab = resize()).length; 32 } 33 if ((p = tab[i = (n - 1) & hash]) == null) { 34 tab[i] = newNode(hash, key, value, null); 35 }else { 36 Node<K,V> e; K k; 37 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) { 38 e = p; 39 }else if (p instanceof TreeNode) { 40 e = ((TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value); 41 } 42 else { 43 for (int binCount = 0; ; ++binCount) { 44 if ((e = p.next) == null) { 45 p.next = newNode(hash, key, value, null); 46 if (binCount >= TREEIFY_THRESHOLD - 1) { // -1 for 1st 47 treeifyBin(tab, hash); 48 } 49 break; 50 } 51 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) { 52 break; 53 } 54 p = e; 55 } 56 } 57 if (e != null) { // existing mapping for key 58 V oldValue = e.value; 59 if (!onlyIfAbsent || oldValue == null) { 60 e.value = value; 61 } 62 afterNodeAccess(e); 63 return oldValue; 64 } 65 } 66 ++modCount; 67 if (++size > threshold) { 68 resize(); 69 } 70 afterNodeInsertion(evict); 71 return null; 72 }