轉載請聲明出處:http://blog.csdn.net/qq_24692041/article/details/65438687
我們先說說HashMap的幾個特點,然後再來慢慢分析!
- HashMap是基於Hash 表實現的。Hash表其實是一個Hash數組+多個單鏈表組成。HashMap會將Key算出hash值,然後每一個hash值對應一個單鏈表,單鏈表中的每一個節點就用於我們存入的數據。然後將這些單鏈表的頭結點存放在Hash數組中。
這就是hash表的結構,紫色的是hash數組,就是源碼中的table。綠色的是鏈表,就是HashMapEntry節點連接起來的。數據的存儲方式就是將=計算key值的hash值,然後將hash值相等的數據存放在同一個鏈表中(注意:hash值相等不代表key值相等,具體的可以去看看hash值的算法)。對於鏈表認識的不夠的小夥伴可以去《數據結構之Java單鏈表反轉》一文中看看,保證讓你看懂! - HashMap通過實現Map接口,提過所有對map的操作。
- HashMap允許key值和value值爲null,並且將爲null的key存放在hash數組的第1個(下標爲0)也即是table[0]的鏈表中。
- HashMap不保證操作(根據插入的順序取出)的順序,因爲在插入的時候內部是通過計算key值的hash值,將hash值相同的數據存放在相同的鏈表中,並且是通過hash值來排序的。並不是跟鏈表一樣,保證插入的順序。
- HashMap性能方面主要考慮capacity容量和loadFactor加載因子,默認容量capacity爲4,默認加載因子loadFactor爲 0.75。如果沒有手動設置這兩個值就會用默認值。capacity表示hash數組也就是table的長度,當容量不夠的時候通過調用resize方法擴容。loadFactor加載因子代表數據的散列情況,值越大散列的越好,但是遍歷速度越慢,因爲鏈表的長度變長了。
- HashMap實現了Serializable接口,因此它支持序列化,實現了Cloneable接口,能被克隆。
- HashMap除了key值和value值允許爲空,不支持線程安全外跟HashTable基本等同。
- hash數組的容量一定是2的偶數次冪,因爲hashMap是將數據根據key值算出hash值,創建鏈表,然後散列在table表中。所以計算出來的這個索引必須可以是奇數也可是偶數,才能完全充分的利用內存,否則內存只能使用一半。而計算索引的indexFor方法中的算法是固定的,必須保證table數組的length是2的偶數次冪才能保證這一點。
下面我們摘取一部分源代碼,具體的來看看,搞清楚HashMap內部的結構和具體的原理,建議網友在看源代碼的時候以構造函數,put,get方法爲入口順藤摸瓜,一點點看下去就簡單了:
public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable { /** * map的默認容量 */ static final int DEFAULT_INITIAL_CAPACITY = 4; /** * 最大容量,2的30次方,當存入過大時,將使用這個容量,並且保證是2的偶數次方 */ static final int MAXIMUM_CAPACITY = 1 << 30; /** * 加載因子默認值,代表着map的散列情況 */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** * 空表 */ static final HashMapEntry<?, ?>[] EMPTY_TABLE = {}; /** * hash數組用於存儲key值計算出來的hash值對應的鏈表的頭指針,容量必須是2的偶數冪 */ transient HashMapEntry<K, V>[] table = (HashMapEntry<K, V>[]) EMPTY_TABLE; /** * 已經使用的容量槽(已經存放的數據的量) */ transient int size; /** * HashMap的閾值,用於判斷是否需要增加容量 */ // If table == EMPTY_TABLE then this is the initial capacity at which the // table will be created when inflated. int threshold; /** * hash表的實際加載因子 */ // Android-Note: We always use a load factor of 0.75 and ignore any explicitly // selected values. final float loadFactor = DEFAULT_LOAD_FACTOR; /** * HashMap被改變的次數(包括已經存放的值的數量的改變和hashMap內部構造的改變) */ transient int modCount; /** * 構造方法,指定容量和加載因子 */ public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) { initialCapacity = MAXIMUM_CAPACITY; } else if (initialCapacity < DEFAULT_INITIAL_CAPACITY) { initialCapacity = DEFAULT_INITIAL_CAPACITY; } if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); // Android-Note: We always use the default load factor of 0.75f. // This might appear wrong but it's just awkward design. We always call // inflateTable() when table == EMPTY_TABLE. That method will take "threshold" // to mean "capacity" and then replace it with the real threshold (i.e, multiplied with // the load factor). threshold = initialCapacity; init(); } /** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and the default load factor (0.75). * * @param initialCapacity the initial capacity. * @throws IllegalArgumentException if the initial capacity is negative. */ public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * Constructs an empty <tt>HashMap</tt> with the default initial capacity * (16) and the default load factor (0.75). */ public HashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR); } /** * 傳入一個Map集合,會創建一個新的Map集合,並且根據傳進來的Map集合的大小對新的Map擴容,然後將數據全部給新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); inflateTable(threshold); putAllForCreate(m); } /** * 處理擴充hash數組的大小數據,保證容量是2的偶次冪 */ private static int roundUpToPowerOf2(int number) { // assert number >= 0 : "number must be non-negative"; int rounded = number >= MAXIMUM_CAPACITY ? MAXIMUM_CAPACITY : (rounded = Integer.highestOneBit(number)) != 0 ? (Integer.bitCount(number) > 1) ? rounded << 1 : rounded : 1; return rounded; } /** * 對hash數組table擴容 */ private void inflateTable(int toSize) { // Find a power of 2 >= toSize int capacity = roundUpToPowerOf2(toSize); // Android-changed: Replace usage of Math.min() here because this method is // called from the <clinit> of runtime, at which point the native libraries // needed by Float.* might not be loaded. //閾值計算,等於hash數組的容量乘以加載因子 float thresholdFloat = capacity * loadFactor; if (thresholdFloat > MAXIMUM_CAPACITY + 1) { thresholdFloat = MAXIMUM_CAPACITY + 1; } threshold = (int) thresholdFloat; //因爲table還是空數組,所以初始化table table = new HashMapEntry[capacity]; } // internal utilities /** * Initialization hook for subclasses. This method is called * in all constructors and pseudo-constructors (clone, readObject) * after HashMap has been initialized but before any entries have * been inserted. (In the absence of this method, readObject would * require explicit knowledge of subclasses.) */ void init() { } /** * Returns index for hash code h. * 返回傳入的hash值在table數組中的索引 */ static int indexFor(int h, int length) { // assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2"; return h & (length - 1); } /** * Returns the number of key-value mappings in this map. * * @return the number of key-value mappings in this map */ public int size() { return size; } /** * Returns <tt>true</tt> if this map contains no key-value mappings. * * @return <tt>true</tt> if this map contains no key-value mappings */ public boolean isEmpty() { return size == 0; } /** * 獲取key所對應的value值 * * @see #put(Object, Object) */ public V get(Object key) { //如果key爲null,遍歷集合取出key值爲null的value值 if (key == null) return getForNullKey(); //如果Key值不爲null,獲取這個key所在的節點 Entry<K, V> entry = getEntry(key); //通過節點拿到value值返回 return null == entry ? null : entry.getValue(); } /** * key值爲null的數據存放在第1個鏈表也即是table[0]的鏈表, * 從table中取出鏈表遍歷獲取key值爲null的value值 */ private V getForNullKey() { if (size == 0) { return null; } for (HashMapEntry<K, V> e = table[0]; e != null; e = e.next) { if (e.key == null) return e.value; } return null; } /** * 查找key值所在的節點,判斷集合中是否包含該數據 * * @param key The key whose presence in this map is to be tested * @return <tt>true</tt> if this map contains a mapping for the specified * key. */ public boolean containsKey(Object key) { return getEntry(key) != null; } /** * 如果key值爲null,hash直接爲0,存入到table[0]中存放的鏈表。 * 如果不爲0計算key值的hash值,並找到該hash值所在的鏈表,從這個鏈表中找到key所在的節點 */ final Entry<K, V> getEntry(Object key) { if (size == 0) { return null; } //計算傳入的key值的hash值 int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key); //在table中取出在hash值所對應的鏈表,遍歷查找key值所在的節點返回 for (HashMapEntry<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; } /** * 往map中以鍵值對的形式加入數據 */ public V put(K key, V value) { //如果Hash數組是空的,就擴容 if (table == EMPTY_TABLE) { inflateTable(threshold); } //如果key值是空,將該數據存放在table[0]中的鏈表 if (key == null) return putForNullKey(value); //如果不爲null,計算key值的hash值 int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key); //找到hash值在數組中的索引 int i = indexFor(hash, table.length); //根據索引取出鏈表,遍歷鏈表 for (HashMapEntry<K, V> e = table[i]; e != null; e = e.next) { Object k; //如果在該hash值對應的鏈表中已經存在key的節點,直接替換oldValue,並將oldValue返回,結束該方法 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; //在hashMap類中,這個方法什麼也沒做,這個方法是給子類LinkedHashMap用的,用於保證操作排序 e.recordAccess(this); return oldValue; } } /* *如果在hash值所在的鏈表中沒有key值對應的節點,就創建新的節點,將key-value放進去 */ //增加更改次數 modCount++; //增加當前鏈表的節點,將key-value放進去 addEntry(hash, key, value, i); return null; } /** * 將空key值插入到table[0]鏈表中 */ private V putForNullKey(V value) { for (HashMapEntry<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; } /** * This method is used instead of put by constructors and * pseudoconstructors (clone, readObject). It does not resize the table, * check for comodification, etc. It calls createEntry rather than * addEntry. */ private void putForCreate(K key, V value) { int hash = null == key ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key); int i = indexFor(hash, table.length); /** * Look for preexisting entry for key. This will never happen for * clone or deserialize. It will only happen for construction if the * input Map is a sorted map whose ordering is inconsistent w/ equals. */ for (HashMapEntry<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; } } createEntry(hash, key, value, i); } private void putAllForCreate(Map<? extends K, ? extends V> m) { for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) putForCreate(e.getKey(), e.getValue()); } /** * 創建出一個更大的table,將舊的table中的數據全部複製到新的table中 */ void resize(int newCapacity) { HashMapEntry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } //創建如一個新的table HashMapEntry[] newTable = new HashMapEntry[newCapacity]; //複製數據到新的table中 transfer(newTable); //將新的table賦給整個hashMap中的table table = newTable; //更改閾值,在默認最大空間+1和新的容量乘上加載因子中取最小值作爲新的閾值 threshold = (int) Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); } /** * 將舊table中的鏈表和鏈表中的數據全部複製到新table中 */ void transfer(HashMapEntry[] newTable) { int newCapacity = newTable.length; //遍歷table數組 for (HashMapEntry<K, V> e : table) { //取出數組中的鏈表,複製到新的table中 while (null != e) { HashMapEntry<K, V> next = e.next; int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } } } /** * 將一個map中的所有數據全部複製hashMap中 * * @param m mappings to be stored in this map * @throws NullPointerException if the specified map is null */ public void putAll(Map<? extends K, ? extends V> m) { int numKeysToBeAdded = m.size(); if (numKeysToBeAdded == 0) return; if (table == EMPTY_TABLE) { inflateTable((int) Math.max(numKeysToBeAdded * loadFactor, threshold)); } /* * Expand the map if the map if the number of mappings to be added * is greater than or equal to threshold. This is conservative; the * obvious condition is (m.size() + size) >= threshold, but this * condition could result in a map with twice the appropriate capacity, * if the keys to be added overlap with the keys already in this map. * By using the conservative calculation, we subject ourself * to at most one extra resize. */ 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); } for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) put(e.getKey(), e.getValue()); } /** * 通過key值刪除已經存在的對應數據 * */ public V remove(Object key) { Entry<K, V> e = removeEntryForKey(key); return (e == null ? null : e.getValue()); } /** * Removes and returns the entry associated with the specified key * in the HashMap. Returns null if the HashMap contains no mapping * for this key. */ final Entry<K, V> removeEntryForKey(Object key) { if (size == 0) { return null; } int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key); int i = indexFor(hash, table.length); HashMapEntry<K, V> prev = table[i]; HashMapEntry<K, V> e = prev; while (e != null) { HashMapEntry<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; //這個方法在HashMap中跟recordAccess一樣,啥都沒做是爲了給自雷linkedHashMap用的 e.recordRemoval(this); return e; } prev = e; e = next; } return e; } /** * Special version of remove for EntrySet using {@code Map.Entry.equals()} * for matching. */ final Entry<K, V> removeMapping(Object o) { if (size == 0 || !(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 : sun.misc.Hashing.singleWordWangJenkinsHash(key); int i = indexFor(hash, table.length); HashMapEntry<K, V> prev = table[i]; HashMapEntry<K, V> e = prev; while (e != null) { HashMapEntry<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; } /** * 通過將table中的所有鏈表置null來清空當前HashMap */ public void clear() { modCount++; Arrays.fill(table, null); size = 0; } /** * 遍歷table中所有的鏈表 * 通過調用Object的equals方法來判斷是否包含該value值 */ public boolean containsValue(Object value) { if (value == null) return containsNullValue(); HashMapEntry[] tab = table; for (int i = 0; i < tab.length; i++) for (HashMapEntry e = tab[i]; e != null; e = e.next) if (value.equals(e.value)) return true; return false; } /** * 遍歷整個table中所有的鏈表,查找value爲null的數據,只要有一個null就直接返回true */ private boolean containsNullValue() { HashMapEntry[] tab = table; for (int i = 0; i < tab.length; i++) for (HashMapEntry e = tab[i]; e != null; e = e.next) if (e.value == null) return true; return false; } /** * Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and * values themselves are not cloned. * * @return a shallow copy of this map */ public Object clone() { HashMap<K, V> result = null; try { result = (HashMap<K, V>) super.clone(); } catch (CloneNotSupportedException e) { // assert false; } if (result.table != EMPTY_TABLE) { result.inflateTable(Math.min( (int) Math.min( size * Math.min(1 / loadFactor, 4.0f), // we have limits... HashMap.MAXIMUM_CAPACITY), table.length)); } result.entrySet = null; result.modCount = 0; result.size = 0; result.init(); result.putAllForCreate(this); return result; } /** * @hide */ // Android added. static class HashMapEntry<K, V> implements Map.Entry<K, V> { final K key; V value; HashMapEntry<K, V> next; int hash; /** * Creates new entry. */ HashMapEntry(int h, K k, V v, HashMapEntry<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; } 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; } public final int hashCode() { return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue()); } public final String toString() { return getKey() + "=" + getValue(); } /** * 在put方法中調用,在HashMap中沒有做任何操作,主要是給子類LinkedHashMap重寫,用於保證插入順序 */ void recordAccess(HashMap<K, V> m) { } /** * 刪除元素時調用,一樣的在hashMap中沒有做任何操作,爲LinkedHashMap準備 */ void recordRemoval(HashMap<K, V> m) { } } /** * 在已有的鏈表中爲傳入的key值新增一個節點 */ void addEntry(int hash, K key, V value, int bucketIndex) { //如果容量槽不夠用,並且當前操作的鏈表不爲null if ((size >= threshold) && (null != table[bucketIndex])) { //將table的容量擴充爲之前的兩倍 resize(2 * table.length); hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0; //爲當前hash值計算出在table中的索引 bucketIndex = indexFor(hash, table.length); } //創建新節點 createEntry(hash, key, value, bucketIndex); } /** *新創建一個節點,並且將該節點作爲當前操作hash值對應鏈表的頭結點 */ void createEntry(int hash, K key, V value, int bucketIndex) {
//將hash值對應的鏈表在table中取出來
HashMapEntry<K, V> e = table[bucketIndex];//創建一個新節點用於存放新的vey-value,並且將該節點作爲當前操作鏈表的頭結點
table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
size++;
}
......}
好了,到這兒具體的源代碼差不多夠了。足夠搞清楚HashMap內部的構造和原理。下面我們將其中比較重要的幾個方法拿出來,重點講解一下:
put方法,用於往hashMap中以key-value的形式添加新的數據, 首先會判斷當前表示否爲空表,然後會判斷當前插入的key值是否爲null,如果爲null,會將該數據插入到表頭,也即是table[0]所在的鏈表。如果不爲null就會算出hash值,取出hash值對應的鏈表,遍歷這個鏈表,如果該鏈表中已經存在這個key值,就會替換舊的value,如果沒有這個key就會創建一個新的節點存放數據,並且會將新創建的節點放在鏈表的頭結點。
/** * 往map中以鍵值對的形式加入數據 */ public V put(K key, V value) { //如果Hash數組是空的,就擴容 if (table == EMPTY_TABLE) { inflateTable(threshold); } //如果key值是空,將該數據存放在table[0]中的鏈表 if (key == null) return putForNullKey(value); //如果不爲null,計算key值的hash值 int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key); //找到hash值在數組中的索引 int i = indexFor(hash, table.length); //根據索引取出鏈表,遍歷鏈表 for (HashMapEntry<K, V> e = table[i]; e != null; e = e.next) { Object k; //如果在該hash值對應的鏈表中已經存在key的節點,直接替換oldValue,並將oldValue返回,結束該方法 if (e.hash == hash && ((k = e.key) == key || key.equals(k))) { V oldValue = e.value; e.value = value; //在hashMap類中,這個方法什麼也沒做,這個方法是給子類LinkedHashMap用的,用於保證操作排序 e.recordAccess(this); return oldValue; } } /* *如果在hash值所在的鏈表中沒有key值對應的節點,就創建新的節點,將key-value放進去 */ //增加更改次數 modCount++; //增加當前鏈表的節點,將key-value放進去 addEntry(hash, key, value, i); return null; }
addEntry方法:這個方法是用於爲鏈表增加節點的,在put方法中,會先去取出hash值對應的鏈表,遍歷,如果已經對應的key值,就更改舊的value值。但是如果沒有這個key值,就要調用該方法增加一個節點存放新插入的數據。
/** * 在已有的鏈表中爲傳入的key值新增一個節點 */ void addEntry(int hash, K key, V value, int bucketIndex) { //如果容量槽不夠用,並且當前操作的鏈表不爲null if ((size >= threshold) && (null != table[bucketIndex])) { //將table的容量擴充爲之前的兩倍 resize(2 * table.length); hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0; //爲當前hash值計算出在table中的索引 bucketIndex = indexFor(hash, table.length); } //創建新節點 createEntry(hash, key, value, bucketIndex); }
createEntry方法:這個方法纔是創建新的節點的具體實現,取出當前hash值對應的鏈表,創建一個新的節點用於存放新插入的數據,並且會將新的節點設置爲當前鏈表的頭結點。
/** *新創建一個節點,並且將該節點作爲當前操作hash值對應鏈表的頭結點 */ void createEntry(int hash, K key, V value, int bucketIndex) {
//將hash值對應的鏈表在table中取出來
HashMapEntry<K, V> e = table[bucketIndex]; //創建一個新節點用於存放新的vey-value,並且將該節點作爲當前操作鏈表的頭結點
table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
size++;
}
get方法:get方法很簡單,就是計算key的hash值,找到這個鏈表,遍歷鏈表取出值。
/** * 獲取key所對應的value值 * * @see #put(Object, Object) */ public V get(Object key) { //如果key爲null,遍歷集合取出key值爲null的value值 if (key == null) return getForNullKey(); //如果Key值不爲null,獲取這個key所在的節點 Entry<K, V> entry = getEntry(key); //通過節點拿到value值返回 return null == entry ? null : entry.getValue(); }
resize方法:這個方法是用於將整個hash表擴容的,會先創建一個新的hash數組,然後將舊的hash數組中的所有鏈表複製到新的hash表中。這個方法是一個數組的複製過程,這是一個耗時的過程,並且在addEntry方法中調用resize方法的時候是將當前容量擴充兩倍的。所以我們在使用的時候最好先約估一下容量的大小。
/** * 創建出一個更大的table,將舊的table中的數據全部複製到新的table中 */ void resize(int newCapacity) { HashMapEntry[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return; } //創建如一個新的table HashMapEntry[] newTable = new HashMapEntry[newCapacity]; //複製數據到新的table中 transfer(newTable); //將新的table賦給整個hashMap中的table table = newTable; //更改閾值,在默認最大空間+1和新的容量乘上加載因子中取最小值作爲新的閾值 threshold = (int) Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1); }
好了,HashMap基本上就這些東西了。沒有更多的需要將的了!