Java 集合系列10之 HashMap詳細介紹(源碼解析)和使用示例


 

概要

這一章,我們對HashMap進行學習。
我們先對HashMap有個整體認識,然后再學習它的源碼,最后再通過實例來學會使用HashMap。內容包括:
第1部分 HashMap介紹
第2部分 HashMap數據結構
第3部分 HashMap源碼解析(基於JDK1.6.0_45)
    第3.1部分 HashMap的“拉鏈法”相關內容
    第3.2部分 HashMap的構造函數
    第3.3部分 HashMap的主要對外接口
    第3.4部分 HashMap實現的Cloneable接口
    第3.5部分 HashMap實現的Serializable接口
第4部分 HashMap遍歷方式
第5部分 HashMap示例

轉載請注明出處:http://www.cnblogs.com/skywang12345/p/3310835.html

 

第1部分 HashMap介紹

HashMap簡介

HashMap 是一個散列表,它存儲的內容是鍵值對(key-value)映射。
HashMap 繼承於AbstractMap,實現了Map、Cloneable、java.io.Serializable接口。
HashMap 的實現不是同步的,這意味着它不是線程安全的。它的key、value都可以為null。此外,HashMap中的映射不是有序的。

HashMap 的實例有兩個參數影響其性能:“初始容量” 和 “加載因子”。容量 是哈希表中桶的數量,初始容量 只是哈希表在創建時的容量。加載因子 是哈希表在其容量自動增加之前可以達到多滿的一種尺度。當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行 rehash 操作(即重建內部數據結構),從而哈希表將具有大約兩倍的桶數。
通常,默認加載因子是 0.75, 這是在時間和空間成本上尋求一種折衷。加載因子過高雖然減少了空間開銷,但同時也增加了查詢成本(在大多數 HashMap 類的操作中,包括 get 和 put 操作,都反映了這一點)。在設置初始容量時應該考慮到映射中所需的條目數及其加載因子,以便最大限度地減少 rehash 操作次數。如果初始容量大於最大條目數除以加載因子,則不會發生 rehash 操作。

 

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()

 

第2部分 HashMap數據結構

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機制的。

 

第3部分 HashMap源碼解析(基於JDK1.6.0_45)

為了更了解HashMap的原理,下面對HashMap源碼代碼作出分析。
在閱讀源碼時,建議參考后面的說明來建立對HashMap的整體認識,這樣更容易理解HashMap。

  1 package java.util;
  2 import java.io.*;
  3 
  4 public class HashMap<K,V>
  5     extends AbstractMap<K,V>
  6     implements Map<K,V>, Cloneable, Serializable
  7 {
  8 
  9     // 默認的初始容量是16,必須是2的冪。
 10     static final int DEFAULT_INITIAL_CAPACITY = 16;
 11 
 12     // 最大容量(必須是2的冪且小於2的30次方,傳入容量過大將被這個值替換)
 13     static final int MAXIMUM_CAPACITY = 1 << 30;
 14 
 15     // 默認加載因子
 16     static final float DEFAULT_LOAD_FACTOR = 0.75f;
 17 
 18     // 存儲數據的Entry數組,長度是2的冪。
 19     // HashMap是采用拉鏈法實現的,每一個Entry本質上是一個單向鏈表
 20     transient Entry[] table;
 21 
 22     // HashMap的大小,它是HashMap保存的鍵值對的數量
 23     transient int size;
 24 
 25     // HashMap的閾值,用於判斷是否需要調整HashMap的容量(threshold = 容量*加載因子)
 26     int threshold;
 27 
 28     // 加載因子實際大小
 29     final float loadFactor;
 30 
 31     // HashMap被改變的次數
 32     transient volatile int modCount;
 33 
 34     // 指定“容量大小”和“加載因子”的構造函數
 35     public HashMap(int initialCapacity, float loadFactor) {
 36         if (initialCapacity < 0)
 37             throw new IllegalArgumentException("Illegal initial capacity: " +
 38                                                initialCapacity);
 39         // HashMap的最大容量只能是MAXIMUM_CAPACITY
 40         if (initialCapacity > MAXIMUM_CAPACITY)
 41             initialCapacity = MAXIMUM_CAPACITY;
 42         if (loadFactor <= 0 || Float.isNaN(loadFactor))
 43             throw new IllegalArgumentException("Illegal load factor: " +
 44                                                loadFactor);
 45 
 46         // 找出“大於initialCapacity”的最小的2的冪
 47         int capacity = 1;
 48         while (capacity < initialCapacity)
 49             capacity <<= 1;
 50 
 51         // 設置“加載因子”
 52         this.loadFactor = loadFactor;
 53         // 設置“HashMap閾值”,當HashMap中存儲數據的數量達到threshold時,就需要將HashMap的容量加倍。
 54         threshold = (int)(capacity * loadFactor);
 55         // 創建Entry數組,用來保存數據
 56         table = new Entry[capacity];
 57         init();
 58     }
 59 
 60 
 61     // 指定“容量大小”的構造函數
 62     public HashMap(int initialCapacity) {
 63         this(initialCapacity, DEFAULT_LOAD_FACTOR);
 64     }
 65 
 66     // 默認構造函數。
 67     public HashMap() {
 68         // 設置“加載因子”
 69         this.loadFactor = DEFAULT_LOAD_FACTOR;
 70         // 設置“HashMap閾值”,當HashMap中存儲數據的數量達到threshold時,就需要將HashMap的容量加倍。
 71         threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
 72         // 創建Entry數組,用來保存數據
 73         table = new Entry[DEFAULT_INITIAL_CAPACITY];
 74         init();
 75     }
 76 
 77     // 包含“子Map”的構造函數
 78     public HashMap(Map<? extends K, ? extends V> m) {
 79         this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
 80                       DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
 81         // 將m中的全部元素逐個添加到HashMap中
 82         putAllForCreate(m);
 83     }
 84 
 85     static int hash(int h) {
 86         h ^= (h >>> 20) ^ (h >>> 12);
 87         return h ^ (h >>> 7) ^ (h >>> 4);
 88     }
 89 
 90     // 返回索引值
 91     // h & (length-1)保證返回值的小於length
 92     static int indexFor(int h, int length) {
 93         return h & (length-1);
 94     }
 95 
 96     public int size() {
 97         return size;
 98     }
 99 
100     public boolean isEmpty() {
101         return size == 0;
102     }
103 
104     // 獲取key對應的value
105     public V get(Object key) {
106         if (key == null)
107             return getForNullKey();
108         // 獲取key的hash值
109         int hash = hash(key.hashCode());
110         // 在“該hash值對應的鏈表”上查找“鍵值等於key”的元素
111         for (Entry<K,V> e = table[indexFor(hash, table.length)];
112              e != null;
113              e = e.next) {
114             Object k;
115             if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
116                 return e.value;
117         }
118         return null;
119     }
120 
121     // 獲取“key為null”的元素的值
122     // HashMap將“key為null”的元素存儲在table[0]位置!
123     private V getForNullKey() {
124         for (Entry<K,V> e = table[0]; e != null; e = e.next) {
125             if (e.key == null)
126                 return e.value;
127         }
128         return null;
129     }
130 
131     // HashMap是否包含key
132     public boolean containsKey(Object key) {
133         return getEntry(key) != null;
134     }
135 
136     // 返回“鍵為key”的鍵值對
137     final Entry<K,V> getEntry(Object key) {
138         // 獲取哈希值
139         // HashMap將“key為null”的元素存儲在table[0]位置,“key不為null”的則調用hash()計算哈希值
140         int hash = (key == null) ? 0 : hash(key.hashCode());
141         // 在“該hash值對應的鏈表”上查找“鍵值等於key”的元素
142         for (Entry<K,V> e = table[indexFor(hash, table.length)];
143              e != null;
144              e = e.next) {
145             Object k;
146             if (e.hash == hash &&
147                 ((k = e.key) == key || (key != null && key.equals(k))))
148                 return e;
149         }
150         return null;
151     }
152 
153     // 將“key-value”添加到HashMap中
154     public V put(K key, V value) {
155         // 若“key為null”,則將該鍵值對添加到table[0]中。
156         if (key == null)
157             return putForNullKey(value);
158         // 若“key不為null”,則計算該key的哈希值,然后將其添加到該哈希值對應的鏈表中。
159         int hash = hash(key.hashCode());
160         int i = indexFor(hash, table.length);
161         for (Entry<K,V> e = table[i]; e != null; e = e.next) {
162             Object k;
163             // 若“該key”對應的鍵值對已經存在,則用新的value取代舊的value。然后退出!
164             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
165                 V oldValue = e.value;
166                 e.value = value;
167                 e.recordAccess(this);
168                 return oldValue;
169             }
170         }
171 
172         // 若“該key”對應的鍵值對不存在,則將“key-value”添加到table中
173         modCount++;
174         addEntry(hash, key, value, i);
175         return null;
176     }
177 
178     // putForNullKey()的作用是將“key為null”鍵值對添加到table[0]位置
179     private V putForNullKey(V value) {
180         for (Entry<K,V> e = table[0]; e != null; e = e.next) {
181             if (e.key == null) {
182                 V oldValue = e.value;
183                 e.value = value;
184                 e.recordAccess(this);
185                 return oldValue;
186             }
187         }
188         // 這里的完全不會被執行到!
189         modCount++;
190         addEntry(0, null, value, 0);
191         return null;
192     }
193 
194     // 創建HashMap對應的“添加方法”,
195     // 它和put()不同。putForCreate()是內部方法,它被構造函數等調用,用來創建HashMap
196     // 而put()是對外提供的往HashMap中添加元素的方法。
197     private void putForCreate(K key, V value) {
198         int hash = (key == null) ? 0 : hash(key.hashCode());
199         int i = indexFor(hash, table.length);
200 
201         // 若該HashMap表中存在“鍵值等於key”的元素,則替換該元素的value值
202         for (Entry<K,V> e = table[i]; e != null; e = e.next) {
203             Object k;
204             if (e.hash == hash &&
205                 ((k = e.key) == key || (key != null && key.equals(k)))) {
206                 e.value = value;
207                 return;
208             }
209         }
210 
211         // 若該HashMap表中不存在“鍵值等於key”的元素,則將該key-value添加到HashMap中
212         createEntry(hash, key, value, i);
213     }
214 
215     // 將“m”中的全部元素都添加到HashMap中。
216     // 該方法被內部的構造HashMap的方法所調用。
217     private void putAllForCreate(Map<? extends K, ? extends V> m) {
218         // 利用迭代器將元素逐個添加到HashMap中
219         for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
220             Map.Entry<? extends K, ? extends V> e = i.next();
221             putForCreate(e.getKey(), e.getValue());
222         }
223     }
224 
225     // 重新調整HashMap的大小,newCapacity是調整后的單位
226     void resize(int newCapacity) {
227         Entry[] oldTable = table;
228         int oldCapacity = oldTable.length;
229         if (oldCapacity == MAXIMUM_CAPACITY) {
230             threshold = Integer.MAX_VALUE;
231             return;
232         }
233 
234         // 新建一個HashMap,將“舊HashMap”的全部元素添加到“新HashMap”中,
235         // 然后,將“新HashMap”賦值給“舊HashMap”。
236         Entry[] newTable = new Entry[newCapacity];
237         transfer(newTable);
238         table = newTable;
239         threshold = (int)(newCapacity * loadFactor);
240     }
241 
242     // 將HashMap中的全部元素都添加到newTable中
243     void transfer(Entry[] newTable) {
244         Entry[] src = table;
245         int newCapacity = newTable.length;
246         for (int j = 0; j < src.length; j++) {
247             Entry<K,V> e = src[j];
248             if (e != null) {
249                 src[j] = null;
250                 do {
251                     Entry<K,V> next = e.next;
252                     int i = indexFor(e.hash, newCapacity);
253                     e.next = newTable[i];
254                     newTable[i] = e;
255                     e = next;
256                 } while (e != null);
257             }
258         }
259     }
260 
261     // 將"m"的全部元素都添加到HashMap中
262     public void putAll(Map<? extends K, ? extends V> m) {
263         // 有效性判斷
264         int numKeysToBeAdded = m.size();
265         if (numKeysToBeAdded == 0)
266             return;
267 
268         // 計算容量是否足夠,
269         // 若“當前實際容量 < 需要的容量”,則將容量x2。
270         if (numKeysToBeAdded > threshold) {
271             int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
272             if (targetCapacity > MAXIMUM_CAPACITY)
273                 targetCapacity = MAXIMUM_CAPACITY;
274             int newCapacity = table.length;
275             while (newCapacity < targetCapacity)
276                 newCapacity <<= 1;
277             if (newCapacity > table.length)
278                 resize(newCapacity);
279         }
280 
281         // 通過迭代器,將“m”中的元素逐個添加到HashMap中。
282         for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
283             Map.Entry<? extends K, ? extends V> e = i.next();
284             put(e.getKey(), e.getValue());
285         }
286     }
287 
288     // 刪除“鍵為key”元素
289     public V remove(Object key) {
290         Entry<K,V> e = removeEntryForKey(key);
291         return (e == null ? null : e.value);
292     }
293 
294     // 刪除“鍵為key”的元素
295     final Entry<K,V> removeEntryForKey(Object key) {
296         // 獲取哈希值。若key為null,則哈希值為0;否則調用hash()進行計算
297         int hash = (key == null) ? 0 : hash(key.hashCode());
298         int i = indexFor(hash, table.length);
299         Entry<K,V> prev = table[i];
300         Entry<K,V> e = prev;
301 
302         // 刪除鏈表中“鍵為key”的元素
303         // 本質是“刪除單向鏈表中的節點”
304         while (e != null) {
305             Entry<K,V> next = e.next;
306             Object k;
307             if (e.hash == hash &&
308                 ((k = e.key) == key || (key != null && key.equals(k)))) {
309                 modCount++;
310                 size--;
311                 if (prev == e)
312                     table[i] = next;
313                 else
314                     prev.next = next;
315                 e.recordRemoval(this);
316                 return e;
317             }
318             prev = e;
319             e = next;
320         }
321 
322         return e;
323     }
324 
325     // 刪除“鍵值對”
326     final Entry<K,V> removeMapping(Object o) {
327         if (!(o instanceof Map.Entry))
328             return null;
329 
330         Map.Entry<K,V> entry = (Map.Entry<K,V>) o;
331         Object key = entry.getKey();
332         int hash = (key == null) ? 0 : hash(key.hashCode());
333         int i = indexFor(hash, table.length);
334         Entry<K,V> prev = table[i];
335         Entry<K,V> e = prev;
336 
337         // 刪除鏈表中的“鍵值對e”
338         // 本質是“刪除單向鏈表中的節點”
339         while (e != null) {
340             Entry<K,V> next = e.next;
341             if (e.hash == hash && e.equals(entry)) {
342                 modCount++;
343                 size--;
344                 if (prev == e)
345                     table[i] = next;
346                 else
347                     prev.next = next;
348                 e.recordRemoval(this);
349                 return e;
350             }
351             prev = e;
352             e = next;
353         }
354 
355         return e;
356     }
357 
358     // 清空HashMap,將所有的元素設為null
359     public void clear() {
360         modCount++;
361         Entry[] tab = table;
362         for (int i = 0; i < tab.length; i++)
363             tab[i] = null;
364         size = 0;
365     }
366 
367     // 是否包含“值為value”的元素
368     public boolean containsValue(Object value) {
369     // 若“value為null”,則調用containsNullValue()查找
370     if (value == null)
371             return containsNullValue();
372 
373     // 若“value不為null”,則查找HashMap中是否有值為value的節點。
374     Entry[] tab = table;
375         for (int i = 0; i < tab.length ; i++)
376             for (Entry e = tab[i] ; e != null ; e = e.next)
377                 if (value.equals(e.value))
378                     return true;
379     return false;
380     }
381 
382     // 是否包含null值
383     private boolean containsNullValue() {
384     Entry[] tab = table;
385         for (int i = 0; i < tab.length ; i++)
386             for (Entry e = tab[i] ; e != null ; e = e.next)
387                 if (e.value == null)
388                     return true;
389     return false;
390     }
391 
392     // 克隆一個HashMap,並返回Object對象
393     public Object clone() {
394         HashMap<K,V> result = null;
395         try {
396             result = (HashMap<K,V>)super.clone();
397         } catch (CloneNotSupportedException e) {
398             // assert false;
399         }
400         result.table = new Entry[table.length];
401         result.entrySet = null;
402         result.modCount = 0;
403         result.size = 0;
404         result.init();
405         // 調用putAllForCreate()將全部元素添加到HashMap中
406         result.putAllForCreate(this);
407 
408         return result;
409     }
410 
411     // Entry是單向鏈表。
412     // 它是 “HashMap鏈式存儲法”對應的鏈表。
413     // 它實現了Map.Entry 接口,即實現getKey(), getValue(), setValue(V value), equals(Object o), hashCode()這些函數
414     static class Entry<K,V> implements Map.Entry<K,V> {
415         final K key;
416         V value;
417         // 指向下一個節點
418         Entry<K,V> next;
419         final int hash;
420 
421         // 構造函數。
422         // 輸入參數包括"哈希值(h)", "鍵(k)", "值(v)", "下一節點(n)"
423         Entry(int h, K k, V v, Entry<K,V> n) {
424             value = v;
425             next = n;
426             key = k;
427             hash = h;
428         }
429 
430         public final K getKey() {
431             return key;
432         }
433 
434         public final V getValue() {
435             return value;
436         }
437 
438         public final V setValue(V newValue) {
439             V oldValue = value;
440             value = newValue;
441             return oldValue;
442         }
443 
444         // 判斷兩個Entry是否相等
445         // 若兩個Entry的“key”和“value”都相等,則返回true。
446         // 否則,返回false
447         public final boolean equals(Object o) {
448             if (!(o instanceof Map.Entry))
449                 return false;
450             Map.Entry e = (Map.Entry)o;
451             Object k1 = getKey();
452             Object k2 = e.getKey();
453             if (k1 == k2 || (k1 != null && k1.equals(k2))) {
454                 Object v1 = getValue();
455                 Object v2 = e.getValue();
456                 if (v1 == v2 || (v1 != null && v1.equals(v2)))
457                     return true;
458             }
459             return false;
460         }
461 
462         // 實現hashCode()
463         public final int hashCode() {
464             return (key==null   ? 0 : key.hashCode()) ^
465                    (value==null ? 0 : value.hashCode());
466         }
467 
468         public final String toString() {
469             return getKey() + "=" + getValue();
470         }
471 
472         // 當向HashMap中添加元素時,繪調用recordAccess()。
473         // 這里不做任何處理
474         void recordAccess(HashMap<K,V> m) {
475         }
476 
477         // 當從HashMap中刪除元素時,繪調用recordRemoval()。
478         // 這里不做任何處理
479         void recordRemoval(HashMap<K,V> m) {
480         }
481     }
482 
483     // 新增Entry。將“key-value”插入指定位置,bucketIndex是位置索引。
484     void addEntry(int hash, K key, V value, int bucketIndex) {
485         // 保存“bucketIndex”位置的值到“e”中
486         Entry<K,V> e = table[bucketIndex];
487         // 設置“bucketIndex”位置的元素為“新Entry”,
488         // 設置“e”為“新Entry的下一個節點”
489         table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
490         // 若HashMap的實際大小 不小於 “閾值”,則調整HashMap的大小
491         if (size++ >= threshold)
492             resize(2 * table.length);
493     }
494 
495     // 創建Entry。將“key-value”插入指定位置,bucketIndex是位置索引。
496     // 它和addEntry的區別是:
497     // (01) addEntry()一般用在 新增Entry可能導致“HashMap的實際容量”超過“閾值”的情況下。
498     //   例如,我們新建一個HashMap,然后不斷通過put()向HashMap中添加元素;
499     // put()是通過addEntry()新增Entry的。
500     //   在這種情況下,我們不知道何時“HashMap的實際容量”會超過“閾值”;
501     //   因此,需要調用addEntry()
502     // (02) createEntry() 一般用在 新增Entry不會導致“HashMap的實際容量”超過“閾值”的情況下。
503     //   例如,我們調用HashMap“帶有Map”的構造函數,它繪將Map的全部元素添加到HashMap中;
504     // 但在添加之前,我們已經計算好“HashMap的容量和閾值”。也就是,可以確定“即使將Map中
505     // 的全部元素添加到HashMap中,都不會超過HashMap的閾值”。
506     //   此時,調用createEntry()即可。
507     void createEntry(int hash, K key, V value, int bucketIndex) {
508         // 保存“bucketIndex”位置的值到“e”中
509         Entry<K,V> e = table[bucketIndex];
510         // 設置“bucketIndex”位置的元素為“新Entry”,
511         // 設置“e”為“新Entry的下一個節點”
512         table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
513         size++;
514     }
515 
516     // HashIterator是HashMap迭代器的抽象出來的父類,實現了公共了函數。
517     // 它包含“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3個子類。
518     private abstract class HashIterator<E> implements Iterator<E> {
519         // 下一個元素
520         Entry<K,V> next;
521         // expectedModCount用於實現fast-fail機制。
522         int expectedModCount;
523         // 當前索引
524         int index;
525         // 當前元素
526         Entry<K,V> current;
527 
528         HashIterator() {
529             expectedModCount = modCount;
530             if (size > 0) { // advance to first entry
531                 Entry[] t = table;
532                 // 將next指向table中第一個不為null的元素。
533                 // 這里利用了index的初始值為0,從0開始依次向后遍歷,直到找到不為null的元素就退出循環。
534                 while (index < t.length && (next = t[index++]) == null)
535                     ;
536             }
537         }
538 
539         public final boolean hasNext() {
540             return next != null;
541         }
542 
543         // 獲取下一個元素
544         final Entry<K,V> nextEntry() {
545             if (modCount != expectedModCount)
546                 throw new ConcurrentModificationException();
547             Entry<K,V> e = next;
548             if (e == null)
549                 throw new NoSuchElementException();
550 
551             // 注意!!!
552             // 一個Entry就是一個單向鏈表
553             // 若該Entry的下一個節點不為空,就將next指向下一個節點;
554             // 否則,將next指向下一個鏈表(也是下一個Entry)的不為null的節點。
555             if ((next = e.next) == null) {
556                 Entry[] t = table;
557                 while (index < t.length && (next = t[index++]) == null)
558                     ;
559             }
560             current = e;
561             return e;
562         }
563 
564         // 刪除當前元素
565         public void remove() {
566             if (current == null)
567                 throw new IllegalStateException();
568             if (modCount != expectedModCount)
569                 throw new ConcurrentModificationException();
570             Object k = current.key;
571             current = null;
572             HashMap.this.removeEntryForKey(k);
573             expectedModCount = modCount;
574         }
575 
576     }
577 
578     // value的迭代器
579     private final class ValueIterator extends HashIterator<V> {
580         public V next() {
581             return nextEntry().value;
582         }
583     }
584 
585     // key的迭代器
586     private final class KeyIterator extends HashIterator<K> {
587         public K next() {
588             return nextEntry().getKey();
589         }
590     }
591 
592     // Entry的迭代器
593     private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
594         public Map.Entry<K,V> next() {
595             return nextEntry();
596         }
597     }
598 
599     // 返回一個“key迭代器”
600     Iterator<K> newKeyIterator()   {
601         return new KeyIterator();
602     }
603     // 返回一個“value迭代器”
604     Iterator<V> newValueIterator()   {
605         return new ValueIterator();
606     }
607     // 返回一個“entry迭代器”
608     Iterator<Map.Entry<K,V>> newEntryIterator()   {
609         return new EntryIterator();
610     }
611 
612     // HashMap的Entry對應的集合
613     private transient Set<Map.Entry<K,V>> entrySet = null;
614 
615     // 返回“key的集合”,實際上返回一個“KeySet對象”
616     public Set<K> keySet() {
617         Set<K> ks = keySet;
618         return (ks != null ? ks : (keySet = new KeySet()));
619     }
620 
621     // Key對應的集合
622     // KeySet繼承於AbstractSet,說明該集合中沒有重復的Key。
623     private final class KeySet extends AbstractSet<K> {
624         public Iterator<K> iterator() {
625             return newKeyIterator();
626         }
627         public int size() {
628             return size;
629         }
630         public boolean contains(Object o) {
631             return containsKey(o);
632         }
633         public boolean remove(Object o) {
634             return HashMap.this.removeEntryForKey(o) != null;
635         }
636         public void clear() {
637             HashMap.this.clear();
638         }
639     }
640 
641     // 返回“value集合”,實際上返回的是一個Values對象
642     public Collection<V> values() {
643         Collection<V> vs = values;
644         return (vs != null ? vs : (values = new Values()));
645     }
646 
647     // “value集合”
648     // Values繼承於AbstractCollection,不同於“KeySet繼承於AbstractSet”,
649     // Values中的元素能夠重復。因為不同的key可以指向相同的value。
650     private final class Values extends AbstractCollection<V> {
651         public Iterator<V> iterator() {
652             return newValueIterator();
653         }
654         public int size() {
655             return size;
656         }
657         public boolean contains(Object o) {
658             return containsValue(o);
659         }
660         public void clear() {
661             HashMap.this.clear();
662         }
663     }
664 
665     // 返回“HashMap的Entry集合”
666     public Set<Map.Entry<K,V>> entrySet() {
667         return entrySet0();
668     }
669 
670     // 返回“HashMap的Entry集合”,它實際是返回一個EntrySet對象
671     private Set<Map.Entry<K,V>> entrySet0() {
672         Set<Map.Entry<K,V>> es = entrySet;
673         return es != null ? es : (entrySet = new EntrySet());
674     }
675 
676     // EntrySet對應的集合
677     // EntrySet繼承於AbstractSet,說明該集合中沒有重復的EntrySet。
678     private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
679         public Iterator<Map.Entry<K,V>> iterator() {
680             return newEntryIterator();
681         }
682         public boolean contains(Object o) {
683             if (!(o instanceof Map.Entry))
684                 return false;
685             Map.Entry<K,V> e = (Map.Entry<K,V>) o;
686             Entry<K,V> candidate = getEntry(e.getKey());
687             return candidate != null && candidate.equals(e);
688         }
689         public boolean remove(Object o) {
690             return removeMapping(o) != null;
691         }
692         public int size() {
693             return size;
694         }
695         public void clear() {
696             HashMap.this.clear();
697         }
698     }
699 
700     // java.io.Serializable的寫入函數
701     // 將HashMap的“總的容量,實際容量,所有的Entry”都寫入到輸出流中
702     private void writeObject(java.io.ObjectOutputStream s)
703         throws IOException
704     {
705         Iterator<Map.Entry<K,V>> i =
706             (size > 0) ? entrySet0().iterator() : null;
707 
708         // Write out the threshold, loadfactor, and any hidden stuff
709         s.defaultWriteObject();
710 
711         // Write out number of buckets
712         s.writeInt(table.length);
713 
714         // Write out size (number of Mappings)
715         s.writeInt(size);
716 
717         // Write out keys and values (alternating)
718         if (i != null) {
719             while (i.hasNext()) {
720             Map.Entry<K,V> e = i.next();
721             s.writeObject(e.getKey());
722             s.writeObject(e.getValue());
723             }
724         }
725     }
726 
727 
728     private static final long serialVersionUID = 362498820763181265L;
729 
730     // java.io.Serializable的讀取函數:根據寫入方式讀出
731     // 將HashMap的“總的容量,實際容量,所有的Entry”依次讀出
732     private void readObject(java.io.ObjectInputStream s)
733          throws IOException, ClassNotFoundException
734     {
735         // Read in the threshold, loadfactor, and any hidden stuff
736         s.defaultReadObject();
737 
738         // Read in number of buckets and allocate the bucket array;
739         int numBuckets = s.readInt();
740         table = new Entry[numBuckets];
741 
742         init();  // Give subclass a chance to do its thing.
743 
744         // Read in size (number of Mappings)
745         int size = s.readInt();
746 
747         // Read the keys and values, and put the mappings in the HashMap
748         for (int i=0; i<size; i++) {
749             K key = (K) s.readObject();
750             V value = (V) s.readObject();
751             putForCreate(key, value);
752         }
753     }
754 
755     // 返回“HashMap總的容量”
756     int   capacity()     { return table.length; }
757     // 返回“HashMap的加載因子”
758     float loadFactor()   { return loadFactor;   }
759 }
View Code

說明:

在詳細介紹HashMap的代碼之前,我們需要了解:HashMap就是一個散列表,它是通過“拉鏈法”解決哈希沖突的
還需要再補充說明的一點是影響HashMap性能的有兩個參數:初始容量(initialCapacity) 和加載因子(loadFactor)。容量 是哈希表中桶的數量,初始容量只是哈希表在創建時的容量。加載因子 是哈希表在其容量自動增加之前可以達到多滿的一種尺度。當哈希表中的條目數超出了加載因子與當前容量的乘積時,則要對該哈希表進行 rehash 操作(即重建內部數據結構),從而哈希表將具有大約兩倍的桶數。


第3.1部分 HashMap的“拉鏈法”相關內容

3.1.1 HashMap數據存儲數組

transient Entry[] table;

HashMap中的key-value都是存儲在Entry數組中的。

3.1.2 數據節點Entry的數據結構

 1 static class Entry<K,V> implements Map.Entry<K,V> {
 2     final K key;
 3     V value;
 4     // 指向下一個節點
 5     Entry<K,V> next;
 6     final int hash;
 7 
 8     // 構造函數。
 9     // 輸入參數包括"哈希值(h)", "鍵(k)", "值(v)", "下一節點(n)"
10     Entry(int h, K k, V v, Entry<K,V> n) {
11         value = v;
12         next = n;
13         key = k;
14         hash = h;
15     }
16 
17     public final K getKey() {
18         return key;
19     }
20 
21     public final V getValue() {
22         return value;
23     }
24 
25     public final V setValue(V newValue) {
26         V oldValue = value;
27         value = newValue;
28         return oldValue;
29     }
30 
31     // 判斷兩個Entry是否相等
32     // 若兩個Entry的“key”和“value”都相等,則返回true。
33     // 否則,返回false
34     public final boolean equals(Object o) {
35         if (!(o instanceof Map.Entry))
36             return false;
37         Map.Entry e = (Map.Entry)o;
38         Object k1 = getKey();
39         Object k2 = e.getKey();
40         if (k1 == k2 || (k1 != null && k1.equals(k2))) {
41             Object v1 = getValue();
42             Object v2 = e.getValue();
43             if (v1 == v2 || (v1 != null && v1.equals(v2)))
44                 return true;
45         }
46         return false;
47     }
48 
49     // 實現hashCode()
50     public final int hashCode() {
51         return (key==null   ? 0 : key.hashCode()) ^
52                (value==null ? 0 : value.hashCode());
53     }
54 
55     public final String toString() {
56         return getKey() + "=" + getValue();
57     }
58 
59     // 當向HashMap中添加元素時,繪調用recordAccess()。
60     // 這里不做任何處理
61     void recordAccess(HashMap<K,V> m) {
62     }
63 
64     // 當從HashMap中刪除元素時,繪調用recordRemoval()。
65     // 這里不做任何處理
66     void recordRemoval(HashMap<K,V> m) {
67     }
68 }
View Code

從中,我們可以看出 Entry 實際上就是一個單向鏈表。這也是為什么我們說HashMap是通過拉鏈法解決哈希沖突的。
Entry 實現了Map.Entry 接口,即實現getKey(), getValue(), setValue(V value), equals(Object o), hashCode()這些函數。這些都是基本的讀取/修改key、value值的函數。

 

第3.2部分 HashMap的構造函數

HashMap共包括4個構造函數

 1 // 默認構造函數。
 2 public HashMap() {
 3     // 設置“加載因子”
 4     this.loadFactor = DEFAULT_LOAD_FACTOR;
 5     // 設置“HashMap閾值”,當HashMap中存儲數據的數量達到threshold時,就需要將HashMap的容量加倍。
 6     threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
 7     // 創建Entry數組,用來保存數據
 8     table = new Entry[DEFAULT_INITIAL_CAPACITY];
 9     init();
10 }
11 
12 // 指定“容量大小”和“加載因子”的構造函數
13 public HashMap(int initialCapacity, float loadFactor) {
14     if (initialCapacity < 0)
15         throw new IllegalArgumentException("Illegal initial capacity: " +
16                                            initialCapacity);
17     // HashMap的最大容量只能是MAXIMUM_CAPACITY
18     if (initialCapacity > MAXIMUM_CAPACITY)
19         initialCapacity = MAXIMUM_CAPACITY;
20     if (loadFactor <= 0 || Float.isNaN(loadFactor))
21         throw new IllegalArgumentException("Illegal load factor: " +
22                                            loadFactor);
23 
24     // Find a power of 2 >= initialCapacity
25     int capacity = 1;
26     while (capacity < initialCapacity)
27         capacity <<= 1;
28 
29     // 設置“加載因子”
30     this.loadFactor = loadFactor;
31     // 設置“HashMap閾值”,當HashMap中存儲數據的數量達到threshold時,就需要將HashMap的容量加倍。
32     threshold = (int)(capacity * loadFactor);
33     // 創建Entry數組,用來保存數據
34     table = new Entry[capacity];
35     init();
36 }
37 
38 // 指定“容量大小”的構造函數
39 public HashMap(int initialCapacity) {
40     this(initialCapacity, DEFAULT_LOAD_FACTOR);
41 }
42 
43 // 包含“子Map”的構造函數
44 public HashMap(Map<? extends K, ? extends V> m) {
45     this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
46                   DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
47     // 將m中的全部元素逐個添加到HashMap中
48     putAllForCreate(m);
49 }
View Code

 

第3.3部分 HashMap的主要對外接口

3.3.1 clear()

clear() 的作用是清空HashMap。它是通過將所有的元素設為null來實現的。

1 public void clear() {
2     modCount++;
3     Entry[] tab = table;
4     for (int i = 0; i < tab.length; i++)
5         tab[i] = null;
6     size = 0;
7 }
View Code

 

3.3.2 containsKey()

containsKey() 的作用是判斷HashMap是否包含key

public boolean containsKey(Object key) {
    return getEntry(key) != null;
}

containsKey() 首先通過getEntry(key)獲取key對應的Entry,然后判斷該Entry是否為null
getEntry()的源碼如下:

 1 final Entry<K,V> getEntry(Object key) {
 2     // 獲取哈希值
 3     // HashMap將“key為null”的元素存儲在table[0]位置,“key不為null”的則調用hash()計算哈希值
 4     int hash = (key == null) ? 0 : hash(key.hashCode());
 5     // 在“該hash值對應的鏈表”上查找“鍵值等於key”的元素
 6     for (Entry<K,V> e = table[indexFor(hash, table.length)];
 7          e != null;
 8          e = e.next) {
 9         Object k;
10         if (e.hash == hash &&
11             ((k = e.key) == key || (key != null && key.equals(k))))
12             return e;
13     }
14     return null;
15 }
View Code

getEntry() 的作用就是返回“鍵為key”的鍵值對,它的實現源碼中已經進行了說明。
這里需要強調的是:HashMap將“key為null”的元素都放在table的位置0,即table[0]中;“key不為null”的放在table的其余位置!


3.3.3 containsValue()

containsValue() 的作用是判斷HashMap是否包含“值為value”的元素

 1 public boolean containsValue(Object value) {
 2     // 若“value為null”,則調用containsNullValue()查找
 3     if (value == null)
 4         return containsNullValue();
 5 
 6     // 若“value不為null”,則查找HashMap中是否有值為value的節點。
 7     Entry[] tab = table;
 8     for (int i = 0; i < tab.length ; i++)
 9         for (Entry e = tab[i] ; e != null ; e = e.next)
10             if (value.equals(e.value))
11                 return true;
12     return false;
13 }
View Code

從中,我們可以看出containsNullValue()分為兩步進行處理:第一,若“value為null”,則調用containsNullValue()。第二,若“value不為null”,則查找HashMap中是否有值為value的節點。

containsNullValue() 的作用判斷HashMap中是否包含“值為null”的元素

1 private boolean containsNullValue() {
2     Entry[] tab = table;
3     for (int i = 0; i < tab.length ; i++)
4         for (Entry e = tab[i] ; e != null ; e = e.next)
5             if (e.value == null)
6                 return true;
7     return false;
8 }
View Code

 

3.3.4 entrySet()、values()、keySet()

它們3個的原理類似,這里以entrySet()為例來說明。
entrySet()的作用是返回“HashMap中所有Entry的集合”,它是一個集合。實現代碼如下:

 1 // 返回“HashMap的Entry集合”
 2 public Set<Map.Entry<K,V>> entrySet() {
 3     return entrySet0();
 4 }
 5 
 6 // 返回“HashMap的Entry集合”,它實際是返回一個EntrySet對象
 7 private Set<Map.Entry<K,V>> entrySet0() {
 8     Set<Map.Entry<K,V>> es = entrySet;
 9     return es != null ? es : (entrySet = new EntrySet());
10 }
11 
12 // EntrySet對應的集合
13 // EntrySet繼承於AbstractSet,說明該集合中沒有重復的EntrySet。
14 private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
15     public Iterator<Map.Entry<K,V>> iterator() {
16         return newEntryIterator();
17     }
18     public boolean contains(Object o) {
19         if (!(o instanceof Map.Entry))
20             return false;
21         Map.Entry<K,V> e = (Map.Entry<K,V>) o;
22         Entry<K,V> candidate = getEntry(e.getKey());
23         return candidate != null && candidate.equals(e);
24     }
25     public boolean remove(Object o) {
26         return removeMapping(o) != null;
27     }
28     public int size() {
29         return size;
30     }
31     public void clear() {
32         HashMap.this.clear();
33     }
34 }
View Code

 

HashMap是通過拉鏈法實現的散列表。表現在HashMap包括許多的Entry,而每一個Entry本質上又是一個單向鏈表。那么HashMap遍歷key-value鍵值對的時候,是如何逐個去遍歷的呢?


下面我們就看看HashMap是如何通過entrySet()遍歷的。
entrySet()實際上是通過newEntryIterator()實現的。 下面我們看看它的代碼:

 1 // 返回一個“entry迭代器”
 2 Iterator<Map.Entry<K,V>> newEntryIterator()   {
 3     return new EntryIterator();
 4 }
 5 
 6 // Entry的迭代器
 7 private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
 8     public Map.Entry<K,V> next() {
 9         return nextEntry();
10     }
11 }
12 
13 // HashIterator是HashMap迭代器的抽象出來的父類,實現了公共了函數。
14 // 它包含“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3個子類。
15 private abstract class HashIterator<E> implements Iterator<E> {
16     // 下一個元素
17     Entry<K,V> next;
18     // expectedModCount用於實現fast-fail機制。
19     int expectedModCount;
20     // 當前索引
21     int index;
22     // 當前元素
23     Entry<K,V> current;
24 
25     HashIterator() {
26         expectedModCount = modCount;
27         if (size > 0) { // advance to first entry
28             Entry[] t = table;
29             // 將next指向table中第一個不為null的元素。
30             // 這里利用了index的初始值為0,從0開始依次向后遍歷,直到找到不為null的元素就退出循環。
31             while (index < t.length && (next = t[index++]) == null)
32                 ;
33         }
34     }
35 
36     public final boolean hasNext() {
37         return next != null;
38     }
39 
40     // 獲取下一個元素
41     final Entry<K,V> nextEntry() {
42         if (modCount != expectedModCount)
43             throw new ConcurrentModificationException();
44         Entry<K,V> e = next;
45         if (e == null)
46             throw new NoSuchElementException();
47 
48         // 注意!!!
49         // 一個Entry就是一個單向鏈表
50         // 若該Entry的下一個節點不為空,就將next指向下一個節點;
51         // 否則,將next指向下一個鏈表(也是下一個Entry)的不為null的節點。
52         if ((next = e.next) == null) {
53             Entry[] t = table;
54             while (index < t.length && (next = t[index++]) == null)
55                 ;
56         }
57         current = e;
58         return e;
59     }
60 
61     // 刪除當前元素
62     public void remove() {
63         if (current == null)
64             throw new IllegalStateException();
65         if (modCount != expectedModCount)
66             throw new ConcurrentModificationException();
67         Object k = current.key;
68         current = null;
69         HashMap.this.removeEntryForKey(k);
70         expectedModCount = modCount;
71     }
72 
73 }
View Code

當我們通過entrySet()獲取到的Iterator的next()方法去遍歷HashMap時,實際上調用的是 nextEntry() 。而nextEntry()的實現方式,先遍歷Entry(根據Entry在table中的序號,從小到大的遍歷);然后對每個Entry(即每個單向鏈表),逐個遍歷。


3.3.5 get()

get() 的作用是獲取key對應的value,它的實現代碼如下:

 1 public V get(Object key) {
 2     if (key == null)
 3         return getForNullKey();
 4     // 獲取key的hash值
 5     int hash = hash(key.hashCode());
 6     // 在“該hash值對應的鏈表”上查找“鍵值等於key”的元素
 7     for (Entry<K,V> e = table[indexFor(hash, table.length)];
 8          e != null;
 9          e = e.next) {
10         Object k;
11         if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
12             return e.value;
13     }
14     return null;
15 }
View Code

 

3.3.6 put()

put() 的作用是對外提供接口,讓HashMap對象可以通過put()將“key-value”添加到HashMap中

 1 public V put(K key, V value) {
 2     // 若“key為null”,則將該鍵值對添加到table[0]中。
 3     if (key == null)
 4         return putForNullKey(value);
 5     // 若“key不為null”,則計算該key的哈希值,然后將其添加到該哈希值對應的鏈表中。
 6     int hash = hash(key.hashCode());
 7     int i = indexFor(hash, table.length);
 8     for (Entry<K,V> e = table[i]; e != null; e = e.next) {
 9         Object k;
10         // 若“該key”對應的鍵值對已經存在,則用新的value取代舊的value。然后退出!
11         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
12             V oldValue = e.value;
13             e.value = value;
14             e.recordAccess(this);
15             return oldValue;
16         }
17     }
18 
19     // 若“該key”對應的鍵值對不存在,則將“key-value”添加到table中
20     modCount++;
21     addEntry(hash, key, value, i);
22     return null;
23 }
View Code

若要添加到HashMap中的鍵值對對應的key已經存在HashMap中,則找到該鍵值對;然后新的value取代舊的value,並退出!
若要添加到HashMap中的鍵值對對應的key不在HashMap中,則將其添加到該哈希值對應的鏈表中,並調用addEntry()。
下面看看addEntry()的代碼:

 1 void addEntry(int hash, K key, V value, int bucketIndex) {
 2     // 保存“bucketIndex”位置的值到“e”中
 3     Entry<K,V> e = table[bucketIndex];
 4     // 設置“bucketIndex”位置的元素為“新Entry”,
 5     // 設置“e”為“新Entry的下一個節點”
 6     table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
 7     // 若HashMap的實際大小 不小於 “閾值”,則調整HashMap的大小
 8     if (size++ >= threshold)
 9         resize(2 * table.length);
10 }
View Code

addEntry() 的作用是新增Entry。將“key-value”插入指定位置,bucketIndex是位置索引。

說到addEntry(),就不得不說另一個函數createEntry()。createEntry()的代碼如下:

1 void createEntry(int hash, K key, V value, int bucketIndex) {
2     // 保存“bucketIndex”位置的值到“e”中
3     Entry<K,V> e = table[bucketIndex];
4     // 設置“bucketIndex”位置的元素為“新Entry”,
5     // 設置“e”為“新Entry的下一個節點”
6     table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
7     size++;
8 }
View Code

它們的作用都是將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中,它的代碼如下:

 1 public void putAll(Map<? extends K, ? extends V> m) {
 2     // 有效性判斷
 3     int numKeysToBeAdded = m.size();
 4     if (numKeysToBeAdded == 0)
 5         return;
 6 
 7     // 計算容量是否足夠,
 8     // 若“當前實際容量 < 需要的容量”,則將容量x2。
 9     if (numKeysToBeAdded > threshold) {
10         int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
11         if (targetCapacity > MAXIMUM_CAPACITY)
12             targetCapacity = MAXIMUM_CAPACITY;
13         int newCapacity = table.length;
14         while (newCapacity < targetCapacity)
15             newCapacity <<= 1;
16         if (newCapacity > table.length)
17             resize(newCapacity);
18     }
19 
20     // 通過迭代器,將“m”中的元素逐個添加到HashMap中。
21     for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {
22         Map.Entry<? extends K, ? extends V> e = i.next();
23         put(e.getKey(), e.getValue());
24     }
25 }
View Code

 

3.3.8 remove()

remove() 的作用是刪除“鍵為key”元素

 1 public V remove(Object key) {
 2     Entry<K,V> e = removeEntryForKey(key);
 3     return (e == null ? null : e.value);
 4 }
 5 
 6 
 7 // 刪除“鍵為key”的元素
 8 final Entry<K,V> removeEntryForKey(Object key) {
 9     // 獲取哈希值。若key為null,則哈希值為0;否則調用hash()進行計算
10     int hash = (key == null) ? 0 : hash(key.hashCode());
11     int i = indexFor(hash, table.length);
12     Entry<K,V> prev = table[i];
13     Entry<K,V> e = prev;
14 
15     // 刪除鏈表中“鍵為key”的元素
16     // 本質是“刪除單向鏈表中的節點”
17     while (e != null) {
18         Entry<K,V> next = e.next;
19         Object k;
20         if (e.hash == hash &&
21             ((k = e.key) == key || (key != null && key.equals(k)))) {
22             modCount++;
23             size--;
24             if (prev == e)
25                 table[i] = next;
26             else
27                 prev.next = next;
28             e.recordRemoval(this);
29             return e;
30         }
31         prev = e;
32         e = next;
33     }
34 
35     return e;
36 }
View Code

 

第3.4部分 HashMap實現的Cloneable接口

HashMap實現了Cloneable接口,即實現了clone()方法。
clone()方法的作用很簡單,就是克隆一個HashMap對象並返回。

 1 // 克隆一個HashMap,並返回Object對象
 2 public Object clone() {
 3     HashMap<K,V> result = null;
 4     try {
 5         result = (HashMap<K,V>)super.clone();
 6     } catch (CloneNotSupportedException e) {
 7         // assert false;
 8     }
 9     result.table = new Entry[table.length];
10     result.entrySet = null;
11     result.modCount = 0;
12     result.size = 0;
13     result.init();
14     // 調用putAllForCreate()將全部元素添加到HashMap中
15     result.putAllForCreate(this);
16 
17     return result;
18 }
View Code

 

第3.5部分 HashMap實現的Serializable接口

HashMap實現java.io.Serializable,分別實現了串行讀取、寫入功能。
串行寫入函數是writeObject(),它的作用是將HashMap的“總的容量,實際容量,所有的Entry”都寫入到輸出流中。
而串行讀取函數是readObject(),它的作用是將HashMap的“總的容量,實際容量,所有的Entry”依次讀出

 1 // java.io.Serializable的寫入函數
 2 // 將HashMap的“總的容量,實際容量,所有的Entry”都寫入到輸出流中
 3 private void writeObject(java.io.ObjectOutputStream s)
 4     throws IOException
 5 {
 6     Iterator<Map.Entry<K,V>> i =
 7         (size > 0) ? entrySet0().iterator() : null;
 8 
 9     // Write out the threshold, loadfactor, and any hidden stuff
10     s.defaultWriteObject();
11 
12     // Write out number of buckets
13     s.writeInt(table.length);
14 
15     // Write out size (number of Mappings)
16     s.writeInt(size);
17 
18     // Write out keys and values (alternating)
19     if (i != null) {
20         while (i.hasNext()) {
21         Map.Entry<K,V> e = i.next();
22         s.writeObject(e.getKey());
23         s.writeObject(e.getValue());
24         }
25     }
26 }
27 
28 // java.io.Serializable的讀取函數:根據寫入方式讀出
29 // 將HashMap的“總的容量,實際容量,所有的Entry”依次讀出
30 private void readObject(java.io.ObjectInputStream s)
31      throws IOException, ClassNotFoundException
32 {
33     // Read in the threshold, loadfactor, and any hidden stuff
34     s.defaultReadObject();
35 
36     // Read in number of buckets and allocate the bucket array;
37     int numBuckets = s.readInt();
38     table = new Entry[numBuckets];
39 
40     init();  // Give subclass a chance to do its thing.
41 
42     // Read in size (number of Mappings)
43     int size = s.readInt();
44 
45     // Read the keys and values, and put the mappings in the HashMap
46     for (int i=0; i<size; i++) {
47         K key = (K) s.readObject();
48         V value = (V) s.readObject();
49         putForCreate(key, value);
50     }
51 }
View Code

 

第4部分 HashMap遍歷方式

4.1 遍歷HashMap的鍵值對

第一步:根據entrySet()獲取HashMap的“鍵值對”的Set集合。
第二步:通過Iterator迭代器遍歷“第一步”得到的集合。

// 假設map是HashMap對象
// map中的key是String類型,value是Integer類型
Integer integ = null;
Iterator iter = map.entrySet().iterator();
while(iter.hasNext()) {
    Map.Entry entry = (Map.Entry)iter.next();
    // 獲取key
    key = (String)entry.getKey();
        // 獲取value
    integ = (Integer)entry.getValue();
}

4.2 遍歷HashMap的鍵

第一步:根據keySet()獲取HashMap的“鍵”的Set集合。
第二步:通過Iterator迭代器遍歷“第一步”得到的集合。

// 假設map是HashMap對象
// map中的key是String類型,value是Integer類型
String key = null;
Integer integ = null;
Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
        // 獲取key
    key = (String)iter.next();
        // 根據key,獲取value
    integ = (Integer)map.get(key);
}

4.3 遍歷HashMap的值

第一步:根據value()獲取HashMap的“值”的集合。
第二步:通過Iterator迭代器遍歷“第一步”得到的集合。

// 假設map是HashMap對象
// map中的key是String類型,value是Integer類型
Integer value = null;
Collection c = map.values();
Iterator iter= c.iterator();
while (iter.hasNext()) {
    value = (Integer)iter.next();
}

 

遍歷測試程序如下

  1 import java.util.Map;
  2 import java.util.Random;
  3 import java.util.Iterator;
  4 import java.util.HashMap;
  5 import java.util.HashSet;
  6 import java.util.Map.Entry;
  7 import java.util.Collection;
  8 
  9 /*
 10  * @desc 遍歷HashMap的測試程序。
 11  *   (01) 通過entrySet()去遍歷key、value,參考實現函數:
 12  *        iteratorHashMapByEntryset()
 13  *   (02) 通過keySet()去遍歷key、value,參考實現函數:
 14  *        iteratorHashMapByKeyset()
 15  *   (03) 通過values()去遍歷value,參考實現函數:
 16  *        iteratorHashMapJustValues()
 17  *
 18  * @author skywang
 19  */
 20 public class HashMapIteratorTest {
 21 
 22     public static void main(String[] args) {
 23         int val = 0;
 24         String key = null;
 25         Integer value = null;
 26         Random r = new Random();
 27         HashMap map = new HashMap();
 28 
 29         for (int i=0; i<12; i++) {
 30             // 隨機獲取一個[0,100)之間的數字
 31             val = r.nextInt(100);
 32             
 33             key = String.valueOf(val);
 34             value = r.nextInt(5);
 35             // 添加到HashMap中
 36             map.put(key, value);
 37             System.out.println(" key:"+key+" value:"+value);
 38         }
 39         // 通過entrySet()遍歷HashMap的key-value
 40         iteratorHashMapByEntryset(map) ;
 41         
 42         // 通過keySet()遍歷HashMap的key-value
 43         iteratorHashMapByKeyset(map) ;
 44         
 45         // 單單遍歷HashMap的value
 46         iteratorHashMapJustValues(map);        
 47     }
 48     
 49     /*
 50      * 通過entry set遍歷HashMap
 51      * 效率高!
 52      */
 53     private static void iteratorHashMapByEntryset(HashMap map) {
 54         if (map == null)
 55             return ;
 56 
 57         System.out.println("\niterator HashMap By entryset");
 58         String key = null;
 59         Integer integ = null;
 60         Iterator iter = map.entrySet().iterator();
 61         while(iter.hasNext()) {
 62             Map.Entry entry = (Map.Entry)iter.next();
 63             
 64             key = (String)entry.getKey();
 65             integ = (Integer)entry.getValue();
 66             System.out.println(key+" -- "+integ.intValue());
 67         }
 68     }
 69 
 70     /*
 71      * 通過keyset來遍歷HashMap
 72      * 效率低!
 73      */
 74     private static void iteratorHashMapByKeyset(HashMap map) {
 75         if (map == null)
 76             return ;
 77 
 78         System.out.println("\niterator HashMap By keyset");
 79         String key = null;
 80         Integer integ = null;
 81         Iterator iter = map.keySet().iterator();
 82         while (iter.hasNext()) {
 83             key = (String)iter.next();
 84             integ = (Integer)map.get(key);
 85             System.out.println(key+" -- "+integ.intValue());
 86         }
 87     }
 88     
 89 
 90     /*
 91      * 遍歷HashMap的values
 92      */
 93     private static void iteratorHashMapJustValues(HashMap map) {
 94         if (map == null)
 95             return ;
 96         
 97         Collection c = map.values();
 98         Iterator iter= c.iterator();
 99         while (iter.hasNext()) {
100             System.out.println(iter.next());
101        }
102     }
103 }
View Code

 

第5部分 HashMap示例

下面通過一個實例學習如何使用HashMap

 1 import java.util.Map;
 2 import java.util.Random;
 3 import java.util.Iterator;
 4 import java.util.HashMap;
 5 import java.util.HashSet;
 6 import java.util.Map.Entry;
 7 import java.util.Collection;
 8 
 9 /*
10  * @desc HashMap測試程序
11  *        
12  * @author skywang
13  */
14 public class HashMapTest {
15 
16     public static void main(String[] args) {
17         testHashMapAPIs();
18     }
19     
20     private static void testHashMapAPIs() {
21         // 初始化隨機種子
22         Random r = new Random();
23         // 新建HashMap
24         HashMap map = new HashMap();
25         // 添加操作
26         map.put("one", r.nextInt(10));
27         map.put("two", r.nextInt(10));
28         map.put("three", r.nextInt(10));
29 
30         // 打印出map
31         System.out.println("map:"+map );
32 
33         // 通過Iterator遍歷key-value
34         Iterator iter = map.entrySet().iterator();
35         while(iter.hasNext()) {
36             Map.Entry entry = (Map.Entry)iter.next();
37             System.out.println("next : "+ entry.getKey() +" - "+entry.getValue());
38         }
39 
40         // HashMap的鍵值對個數        
41         System.out.println("size:"+map.size());
42 
43         // containsKey(Object key) :是否包含鍵key
44         System.out.println("contains key two : "+map.containsKey("two"));
45         System.out.println("contains key five : "+map.containsKey("five"));
46 
47         // containsValue(Object value) :是否包含值value
48         System.out.println("contains value 0 : "+map.containsValue(new Integer(0)));
49 
50         // remove(Object key) : 刪除鍵key對應的鍵值對
51         map.remove("three");
52 
53         System.out.println("map:"+map );
54 
55         // clear() : 清空HashMap
56         map.clear();
57 
58         // isEmpty() : HashMap是否為空
59         System.out.println((map.isEmpty()?"map is empty":"map is not empty") );
60     }
61 }
View Code

 (某一次)運行結果: 

map:{two=7, one=9, three=6}
next : two - 7
next : one - 9
next : three - 6
size:3
contains key two : true
contains key five : false
contains value 0 : false
map:{two=7, one=9}
map is empty

 


更多內容

Java 集合系列目錄

Java 集合系列01之 總體框架

Java 集合系列10之 HashMap詳細介紹(源碼解析)和使用示例

Java 集合系列11之 Hashtable詳細介紹(源碼解析)和使用示例

Java 集合系列12之 TreeMap詳細介紹(源碼解析)和使用示例

Java 集合系列13之 WeakHashMap詳細介紹(源碼解析)和使用示例

Java 集合系列14之 Map總結(HashMap, Hashtable, TreeMap, WeakHashMap等使用場景)

Java 集合系列18之 Iterator和Enumeration比較

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM