HashMap源代碼詳解

HashMap類聲明

HashMap類聲明如下:

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable {
  //……
  //……
}

從定義中可以看出,哈希表是支持克隆、序列化的。

HashMap繼承了AbstractMap,除了toString方法,HashMap重寫了其他所有的AbstractMap方法。

HashMap實現的克隆是淺複製。

HashMap自定義了序列化相關的方法。

HashMap實現原理

分析HashMap代碼之前先了解它的實現原理,HashMap是通過拉鍊法實現的,何謂拉鍊法?HashMap是一個數組,數組的元素是一個鏈表,鍵值對是存儲在該鏈表中的。通過這種方法能夠解決哈希衝突問題,哈希碼相同的鍵對應的鍵值對會存儲在同一個鏈表中。拉鍊表示例圖如下:

這裏寫圖片描述

該哈希表的容量爲8,鍵值對數量爲6。目前該哈希表中只有索引0、4、7處有數據。其中e0和e1節點鍵的哈希碼相同,e3、e4、e5節點鍵的哈希碼相同。

HashMap中有加載因子的概念,它代表了哈希表有多滿的一種程度,這個值默認是0.75。當哈希表的鍵值對數量大於等於加載因子和哈希表容量乘積的時候,將會觸發重哈希。例如,上圖哈希表重哈希的鍵值對數量臨界值爲6(8*0.75=6)。因爲該哈希表鍵值對數量已經爲6,此時若繼續往該哈希表添加鍵值對,將會觸發重哈希。

加載因子是爲了哈希表的效率而提出的,這會保證哈希表中的鏈表不至於太長,從而減少衝突,增加查詢效率。平均來說,哈希表中每個鏈表長度都將小於1,因此查找效率非常高。加載因子越大,衝突的可能性就越大,加載因子越小,衝突的可能性就越小。

有了實現原理的基本瞭解,接下來分析HashMap的源碼實現。

HashMap源碼分析

成員變量

HashMap包括了很多成員變量,下面代碼詳細註釋了每個字段的含意:

    //哈希表的默認容量是16
    //注意:哈希表容量必須是2的n次冪,這是爲了更快的定位哈希索引
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 
    //哈希表的最大容量
    static final int MAXIMUM_CAPACITY = 1 << 30;
    //哈希表的加載因子默認值0.75
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    //若沒有對哈希表執行任何操作,數組是一個空值,只有在需要的時候纔會去分配內存
    static final Entry<?,?>[] EMPTY_TABLE = {};
    //哈希表的數組,其中每個元素都是一個鏈表,哈希表數組大小必須是2的n次冪
    transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
    //哈希表的鍵值對數量
    transient int size;
    //哈希表擴容的臨界值,大小等於加載因子*哈希表的容量
    int threshold;
    //加載因子
    final float loadFactor;
    //哈希表結構性改變的次數,結構性改變指的是改變了哈希表鍵值對數量
    //該字段主要用在迭代器的快速失敗策略(fail-fast),後面還會說明
    transient int modCount;
    //默認擴容的臨界值
    static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;
    //一個隨機的值,用於計算key的哈希值
    transient int hashSeed = 0;
    //鍵值對的Set
    private transient Set<Map.Entry<K,V>> entrySet = null;

Holder靜態內部類

//HashMap的靜態內部類,主要是去獲取jdk內置的擴容臨界值
private static class Holder {

    static final int ALTERNATIVE_HASHING_THRESHOLD;

    static {
      //取jdk內置的擴容臨界值
      String altThreshold = java.security.AccessController.doPrivileged(
        new sun.security.action.GetPropertyAction(
          "jdk.map.althashing.threshold"));

      int threshold;
      try {
        threshold = (null != altThreshold)
          ? Integer.parseInt(altThreshold)
          : ALTERNATIVE_HASHING_THRESHOLD_DEFAULT;

        // disable alternative hashing if -1
        if (threshold == -1) {
          threshold = Integer.MAX_VALUE;
        }

        if (threshold < 0) {
          throw new IllegalArgumentException("value must be positive integer.");
        }
      } catch(IllegalArgumentException failed) {
        throw new Error("Illegal value for 'jdk.map.althashing.threshold'", failed);
      }

      ALTERNATIVE_HASHING_THRESHOLD = threshold;
    }
}

構造函數

//包含初始容易和加載因子的構造函數
public HashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
      throw new IllegalArgumentException("Illegal initial capacity: " +
                                         initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
      //HashMap的容量不能大於MAXMUM_CAPACITY
      initialCapacity = MAXIMUM_CAPACITY;
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
      throw new IllegalArgumentException("Illegal load factor: " +
                                         loadFactor);
    this.loadFactor = loadFactor;
    threshold = initialCapacity;
    //HashMap的init方法其實是個空方法
    init();
}
//根據指定初始容量、默認加載因子創建一個空的HashMap
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
//根據默認容量和默認加載因子創建一個空的HashMap
public HashMap() {
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
//根據指定的HashMap創建一個新的HashMap,新的HashMap將擁有指定HashMap所有的鍵值對。
//新HashMap的初始容量根據指定HashMap的鍵值對數量決定,加載因子取默認值
public HashMap(Map<? extends K, ? extends V> m) {
    this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                  DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
    //創建好空的HashMap後,根據擴容臨界值擴容該HashMap
    inflateTable(threshold);
    //將指定哈希表m的所有鍵值對添加到該哈希表中
    putAllForCreate(m);
}

擴容方法

//大於等於number的第一個2的n次冪的值,這個保證了哈希表的容量是2的n次冪
private static int roundUpToPowerOf2(int number) {
    return number >= MAXIMUM_CAPACITY
      ? MAXIMUM_CAPACITY
      : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}

//擴容哈希表
private void inflateTable(int toSize) {
    int capacity = roundUpToPowerOf2(toSize);

    threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
    table = new Entry[capacity];
    //初始化哈希種子
    initHashSeedAsNeeded(capacity);
}

Integer.highestOneBit方法

上面代碼片斷使用了Integer.highestOneBit方法,這片代碼挺有意思的,看下源碼實現:

public static int highestOneBit(int i) {
    i |= (i >>  1);
    i |= (i >>  2);
    i |= (i >>  4);
    i |= (i >>  8);
    i |= (i >> 16);
    return i - (i >>> 1);
}

這個代碼片斷返回不大於參數i的最大的2的n次冪的值。例如如果傳入參數17,返回16(2的4次冪)。傳入參數16,返回16。傳入參數15,返回8。(確實很巧妙~)

這斷代碼怎麼實現的呢?是將參數i的二進制最左邊爲1的位的後面都設置爲0而得出的,例如對於10進制數17,它的二進制爲10001,將最高位1的右邊都設置爲0,變爲10000,也就是10進制16。同樣對於10進制數15,它的二進制數爲1111,將最高位1的右邊都設置爲0,變爲1000,也就是10進制8。

再來看這斷代碼的實現,首先將i右移1位並和原來的i作或操作,結果就是最高位爲1的後面那一位也將變爲1,現在最高位爲1的連續兩位都是1。接着繼續將i右移2位並和原來的i作或操作,結果就是最高位爲1的連續4位都是1。以此類推,直到最高位爲1的後面都變爲1。最後將該值減去將最高位設置爲0的值,也就只保留了最高位爲1的值。

例如對於i=10001(10進製爲17):

  • i |= (i >> 1),即i = 10001 | (10001 >> 1),即i = 10001|01000=11001,此時i=11001

  • i |= (i >> 2),即i = 11001 | (11001 >> 2),即i = 11001|00110=11111,此時i=11111

  • i |= (i >> 4),即i = 11111 | (11111 >> 4),即i = 11111|00001=11111,此時i=11111

  • i |= (i >> 8),即i = 11111 | (11111 >> 8),即i = 11111|00000=11111,此時i=11111

  • i |= (i >> 16),即i = 11111 | (11111 >> 16),即i = 11111|00000=11111,此時i=11111

  • i - (i >>> 1),即11111-01111=10000,10進制即是16

HaspMap中的roundUpToPowerOf2方法,傳入給highestOneBit的是參數number減1的值,爲何需要減1呢?

當參數number的二進制位中不止一個位是1時,此時將該值減1不影響最高位爲1的值,因此減1後的值左移1位後最高位爲1的就有了兩個連續1,調用highestOneBit方法後將最高位的1後面所有值設置爲0。得到的值就是符合要求的值。

當參數number的二進制位中只有一個位是1時,那麼該值肯定是2的n次冪。其實直接返回該值就好了,此處減1後左移1位,再調用highestOneBit實現的效果即是得到number值。

好了,繼續研究HashMap源代碼:

哈希碼計算和哈希表的定位

//int方法是空實現
void init() {
}
//初始化hashSeed的值
final boolean initHashSeedAsNeeded(int capacity) {
    boolean currentAltHashing = hashSeed != 0;
    boolean useAltHashing = sun.misc.VM.isBooted() &&
      (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
    boolean switching = currentAltHashing ^ useAltHashing;
    if (switching) {
      hashSeed = useAltHashing
        ? sun.misc.Hashing.randomHashSeed(this)
        : 0;
    }
    return switching;
}
//這斷代碼計算鍵的哈希碼
//如果鍵是字符串,會調用特定的方法去計算字符串的哈希碼
final int hash(Object k) {
    int h = hashSeed;
    if (0 != h && k instanceof String) {
      return sun.misc.Hashing.stringHash32((String) k);
    }
    h ^= k.hashCode();
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
//通過給定的哈希碼h和哈希表長度length計算該哈希碼所在表的位置
//其中length必須是2的n次冪,這樣可以更快定位
static int indexFor(int h, int length) {
    //等價於h%length
    return h & (length-1);
}
//返回哈希表鍵值對數量
public int size() {
    return size;
}
//返回哈希表是否爲空
public boolean isEmpty() {
     return size == 0;
}

get和contains相關方法

//返回鍵key對應的值value,若不存在返回null
public V get(Object key) {
    if (key == null)
      //key爲null直接調用了getForNullKey方法
      return getForNullKey();
    //通過鍵key獲得鍵值對
    Entry<K,V> entry = getEntry(key);

    return null == entry ? null : entry.getValue();
}
//處理鍵key爲null的情況
private V getForNullKey() {
    if (size == 0) {
      return null;
    }
    //查找哈希表索引0處的鏈表,可以看出key爲null的鍵值對一定存儲在哈希表的索引0上
    for (Entry<K,V> e = table[0]; e != null; e = e.next) {
      if (e.key == null)
        return e.value;
    }
    return null;
}
//返回鍵key是否存在
public boolean containsKey(Object key) {
    return getEntry(key) != null;
}
//返回鍵key對應的鍵值對,若不存在返回null
final Entry<K,V> getEntry(Object key) {
    if (size == 0) {
      return null;
    }
    //計算鍵key的哈希碼
    int hash = (key == null) ? 0 : hash(key);
    //首先根據哈希碼定位到哈希表中的索引,若鍵key存在則一定存在於該索引對應的鏈表中
    for (Entry<K,V> e = table[indexFor(hash, table.length)];
         e != null;
         e = e.next) {
      Object k;
      //比較Entry的hash值和鍵key的哈希值是否相等、比較當前鍵值對的鍵是否和key相等
      if (e.hash == hash &&
          ((k = e.key) == key || (key != null && key.equals(k))))
        return e;
    }
    return null;
}

put相關方法

//將鍵key、值value添加到哈希表中
//返回之前key關聯的值,若之前key不存在則返回null
public V put(K key, V value) {
    //因爲初始化時的哈希表是一個空的哈希表,當需要的時候就要擴容
    if (table == EMPTY_TABLE) {
      inflateTable(threshold);
    }
    if (key == null)
      //鍵key爲null時走putForNullKey方法
      return putForNullKey(value);
    //取鍵key的哈希碼並定位到哈希表中的索引
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
      Object k;
      if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
        //此處說明鍵key已經存在,用新值value代替舊值oldValue
        V oldValue = e.value;
        e.value = value;
        //記錄本次操作,這裏的recordAccess其實是個空實現
        e.recordAccess(this);
        return oldValue;
      }
    }
    //每次對哈希表的結構性改變時都會遞增modCount的值
    modCount++;
    //鍵key不存在,添加新的鍵值對到哈希表中,鍵值對的hash屬性就是key的哈希碼值
    addEntry(hash, key, value, i);
    //因爲鍵值對不存在,所以返回null
    return null;
}
//鍵爲null情況下的put操作
private V putForNullKey(V value) {
    //始終將key爲null的鍵值對添加到哈希表table的索引0處
    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;
}
//添加鍵值對,該私有方法不會對哈希表進行擴容,並且該方法不會記錄modCount
//該方法僅在通過另一個哈希表來創建哈希表的時候使用
private void putForCreate(K key, V value) {
    int hash = null == key ? 0 : hash(key);
    int i = indexFor(hash, table.length);
    //查找之前已經存在的鍵爲key的鍵值對,並將用新的value代替舊的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;
      }
    }
    //新建一個新的鍵值對
    createEntry(hash, key, value, i);
}
//該方法很簡單,將哈希表m中的鍵值對添加到本哈希表中
private void putAllForCreate(Map<? extends K, ? extends V> m) {
    for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
      putForCreate(e.getKey(), e.getValue());
}

重哈希: resize和transfer方法

//當哈希表鍵值對數量達到threshold的時候自動重哈希,重哈希後的哈希表容量變得更大
//newCapacity是重哈希後新的容量
void resize(int newCapacity) {
    Entry[] oldTable = table;
    int oldCapacity = oldTable.length;
    //哈希表的最大容量是MAXIMUN_CAPACITY,達到該值後不會再重哈希
    if (oldCapacity == MAXIMUM_CAPACITY) {
      threshold = Integer.MAX_VALUE;
      return;
    }
    //根據新的容量爲哈希表重新分配內存
    Entry[] newTable = new Entry[newCapacity];
    //將老的哈希表所有鍵值對遷移到新的哈希表
    transfer(newTable, initHashSeedAsNeeded(newCapacity));
    table = newTable;
    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}
//將老的哈希表中所有的鍵值對遷移到新的哈希表中
void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    //遍歷老的哈希表
    for (Entry<K,V> e : table) {
      //哈希表的每一個元素都是一個鏈表,遍歷該鏈表
      while(null != e) {
        Entry<K,V> next = e.next;
        //如果需要重新計算鍵的哈希值
        if (rehash) {
          e.hash = null == e.key ? 0 : hash(e.key);
        }
        //計算鍵在新的哈希表中的索引
        int i = indexFor(e.hash, newCapacity);
        //這三行代碼完成了鍵值對的遷移
        e.next = newTable[i];
        newTable[i] = e;
        e = next;
      }
    }
}

上面代碼片斷功能是遷移老的哈希表所有的鍵值對到新的哈希表,核心思想是就是上面最底行的三行代碼,我們分析下,假設老的哈希表如下圖示:

這裏寫圖片描述

老的哈希表的容量爲4,假設擴容後的哈希表容量爲8,並且e0重哈希後到新哈希表的索引3處:
這裏寫圖片描述

可以看到,這三行代碼執行完成後就將e0插入到新哈希表的索引3處,並且處於索引3處鏈表的頭。

繼續看源代碼:

//該方法將哈希表m的所有鍵對加入到本哈希表中
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));
     }
     //計算是否要擴容、重哈希
     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的所有鍵值對添加到本哈希表中
     for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
       put(e.getKey(), e.getValue());
}

remove相關方法

//刪除鍵key對應的鍵值對
//返回之前key關聯的值,如果不存在,返回null
public V remove(Object key) {
    //其實是調用removeEntryForKey方法刪除的
    Entry<K,V> e = removeEntryForKey(key);
    return (e == null ? null : e.value);
}
//刪除鍵key對應的鍵值對
final Entry<K,V> removeEntryForKey(Object key) {
    if (size == 0) {
      return null;
    }
    int hash = (key == null) ? 0 : hash(key);
    int i = indexFor(hash, table.length);
    Entry<K,V> prev = table[i];
    Entry<K,V> e = prev;

    while (e != null) {
      Entry<K,V> next = e.next;
      Object k;
      if (e.hash == hash &&
          ((k = e.key) == key || (key != null && key.equals(k)))) {
        //找到了鍵爲key的鍵值對,執行刪除操作
        //這裏其實就是執行鏈表的刪除操作,修改相關的指針即可
        modCount++;
        size--;
        //要刪除的結點就是第一個結點
        if (prev == e)
          table[i] = next;
        else
          prev.next = next;
        e.recordRemoval(this);
        return e;
      }
      prev = e;
      e = next;
    }

    return e;
}
//刪除指定的鍵值對,這裏的o必須是一個Map.Entry 
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 : hash(key);
     int i = indexFor(hash, table.length);
     Entry<K,V> prev = table[i];
     Entry<K,V> e = prev;

     while (e != null) {
       Entry<K,V> next = e.next;
       //鍵和值都相等時纔會刪除
       if (e.hash == hash && e.equals(entry)) {
         modCount++;
         size--;
         if (prev == e)
           table[i] = next;
         else
           prev.next = next;
         e.recordRemoval(this);
         return e;
       }
       prev = e;
       e = next;
     }

     return e;
}
//清空哈希表
public void clear() {
    modCount++;
    //將table每個值設爲null
    Arrays.fill(table, null);
    size = 0;
}
//返回哈希表中是否包含值value
public boolean containsValue(Object value) {
     if (value == null)
       //是否有空的值
       return containsNullValue();

     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;
}

clone-淺複製

//克隆方法,實現了淺複製
public Object clone() {
    HashMap<K,V> result = null;
    try {
      //調用父類的clone方法
      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中
    result.putAllForCreate(this);
    return result;
}

HashMap.Entry靜態內部類

//鍵值對,實現了Map.Entry接口
//這裏的Entry其實是一個鏈表結點,每個結點有鍵、值、next指針、哈希碼
static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    Entry<K,V> next;
    int hash;

    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;
    }
    //equals方法比較了鍵是否相等、值是否相等,只有鍵和值都相等時兩個Entry才相等
    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;
    }
    //鍵值對的哈希碼,需要和hash屬性區別開來,這裏的hash存儲的是鍵的哈希碼
    public final int hashCode() {
      return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
    }

    public final String toString() {
      return getKey() + "=" + getValue();
    }

    void recordAccess(HashMap<K,V> m) {
    }

    void recordRemoval(HashMap<K,V> m) {
    }
}

addEntry方法

//增加鍵值對、需要考慮重哈希
void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {
      //新哈希表的容量擴充爲原來的2倍
      resize(2 * table.length);
      hash = (null != key) ? hash(key) : 0;
      bucketIndex = indexFor(hash, table.length);
    }

    createEntry(hash, key, value, bucketIndex);
}
//創建鍵值對
void createEntry(int hash, K key, V value, int bucketIndex) {
    //每次將新的鍵值對添加到鏈表的表頭
    Entry<K,V> e = table[bucketIndex];
    table[bucketIndex] = new Entry<>(hash, key, value, e);
    size++;
}

HashMap內部迭代器以及迭代器快速失敗機制

//HashMap使用到的迭代器的抽象基類
private abstract class HashIterator<E> implements Iterator<E> {
    Entry<K,V> next;       
    //通過expectedModCount實現迭代器的快速失敗機制
    int expectedModCount;   
    //迭代器當前所在的哈希表索引
    int index;              
    Entry<K,V> current;     

    HashIterator() {
      //expectedModCount初始化爲HashMap的成員變量modCount
      expectedModCount = modCount;
      if (size > 0) { 
        //找到第一個有值的鏈表
        Entry[] t = table;
        while (index < t.length && (next = t[index++]) == null)
          ;
      }
    }

    public final boolean hasNext() {
      return next != null;
    }

    final Entry<K,V> nextEntry() {
      //說明在迭代器迭代的時候HashMap已經被結構性改變,拋出併發修改異常
      if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
      Entry<K,V> e = next;
      if (e == null)
        throw new NoSuchElementException();
      //返回Entry前,需要準備好下一次迭代的Entry
      if ((next = e.next) == null) {
        Entry[] t = table;
        while (index < t.length && (next = t[index++]) == null)
          ;
      }
      current = e;
      return e;
    }
    //刪除操作,調用HashMap.removeEntryForKey方法實現的
    public void remove() {
      //還沒有開始遍歷、或者沒有任何節點、或者已經刪除,current爲空
      if (current == null)
        throw new IllegalStateException();
      if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
      Object k = current.key;
      //刪除後將current設置爲空,防止重複刪除
      current = null;
      HashMap.this.removeEntryForKey(k);
      //刪除後更新expectedModCount的值,不然會拋出ConcurrentModificationException
      expectedModCount = modCount;
    }
}
//值集合的迭代器,靜態內部類
private final class ValueIterator extends HashIterator<V> {
    public V next() {
      return nextEntry().value;
    }
}
//鍵集合的迭代器,靜態內部類
private final class KeyIterator extends HashIterator<K> {
    public K next() {
      return nextEntry().getKey();
    }
}
//鍵值對的迭代器,靜態內部類
private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
    public Map.Entry<K,V> next() {
      return nextEntry();
    }
}
//返回一個鍵集合的迭代器
Iterator<K> newKeyIterator()   {
    return new KeyIterator();
}
//返回一個值集合的迭代器
Iterator<V> newValueIterator()   {
    return new ValueIterator();
}
//返回一個鍵值對集合的迭代器
Iterator<Map.Entry<K,V>> newEntryIterator()   {
    return new EntryIterator();
}

鍵、值、鍵值對集合

//返回鍵的Set集合
public Set<K> keySet() {
    Set<K> ks = keySet;
    return (ks != null ? ks : (keySet = new KeySet()));
}
//鍵的集合,繼承自AbstractSet的內部類
private final class KeySet extends AbstractSet<K> {
    //返回一個鍵的迭代器
    public Iterator<K> iterator() {
      return newKeyIterator();
    }
    //鍵的數量
    public int size() {
      return size;
    }
    //是否存在鍵o
    public boolean contains(Object o) {
      return containsKey(o);
    }
    //刪除鍵o對應的鍵值對
    public boolean remove(Object o) {
      return HashMap.this.removeEntryForKey(o) != null;
    }
    //清空
    public void clear() {
      HashMap.this.clear();
    }
}
//返回值的集合
public Collection<V> values() {
    Collection<V> vs = values;
    return (vs != null ? vs : (values = new Values()));
}
//值的集合,繼承自AbstractCollection的內部類
private final class Values extends AbstractCollection<V> {
    //返回一個值的迭代器
    public Iterator<V> iterator() {
      return newValueIterator();
    }
    public int size() {
      return size;
    }
    public boolean contains(Object o) {
      return containsValue(o);
    }
    public void clear() {
      HashMap.this.clear();
    }
}
//返回鍵值對的Set集合
public Set<Map.Entry<K,V>> entrySet() {
    return entrySet0();
}
private Set<Map.Entry<K,V>> entrySet0() {
    Set<Map.Entry<K,V>> es = entrySet;
    return es != null ? es : (entrySet = new EntrySet());
}
//鍵值對的集合,繼承自AbstractSet的內部類
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();
    }
}

writeObject和readObject序列化相關方法

另外,HashMap也實現了writeObject和readObject方法,實現自定義的序列化:

//序列化方法
private void writeObject(java.io.ObjectOutputStream s)
  throws IOException
{
    //默認序列化
    s.defaultWriteObject();

    //序列化哈希表的大小
    if (table==EMPTY_TABLE) {
      s.writeInt(roundUpToPowerOf2(threshold));
    } else {
      s.writeInt(table.length);
    }
    //序列化哈希表鍵值對數量
    s.writeInt(size);
    //序列化鍵值對
    if (size > 0) {
      for(Map.Entry<K,V> e : entrySet0()) {
        s.writeObject(e.getKey());
        s.writeObject(e.getValue());
      }
    }
}

//反序列化方法
private void readObject(java.io.ObjectInputStream s)
         throws IOException, ClassNotFoundException
    {
    // 默認反序列化
    s.defaultReadObject();
    if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
      throw new InvalidObjectException("Illegal load factor: " +
                                       loadFactor);
    }

    table = (Entry<K,V>[]) EMPTY_TABLE;

    // 反序列化哈希表的大小
    s.readInt();

    // 反序列化哈希表鍵值對數量
    int mappings = s.readInt();
    if (mappings < 0)
      throw new InvalidObjectException("Illegal mappings count: " +
                                       mappings);

    int capacity = (int) Math.min(
      mappings * Math.min(1 / loadFactor, 4.0f),
      HashMap.MAXIMUM_CAPACITY);

    if (mappings > 0) {
      inflateTable(capacity);
    } else {
      threshold = capacity;
    }

    init(); 
    //反序列化鍵值對
    for (int i = 0; i < mappings; i++) {
      K key = (K) s.readObject();
      V value = (V) s.readObject();
      putForCreate(key, value);
    }
}
//返回哈希表的容量,也就是table數組的大小
int   capacity()     { return table.length; }
//返回加載因子
float loadFactor()   { return loadFactor;   }

到此爲止,HashMap源碼分析完了

參考:

  1. jdk源碼
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章