Java中的HashMap

1、 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 操做。java

HashMap的繼承關係

Java中的HashMap
HashMap與Map關係以下圖:
Java中的HashMap數組

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的原理,下面對HashMap源碼代碼做出分析。app

package java.util;

import java.io.*;

public class HashMap<K,V>

     extends AbstractMap<K,V>

     implements Map<K,V>, Cloneable, Serializable

{

​ // 默認的初始容量是16,必須是2的冪。ide

​ static final int DEFAULT_INITIAL_CAPACITY = 16;函數

​ // 最大容量(必須是2的冪且小於2的30次方,傳入容量過大將被這個值替換)性能

​ static final int MAXIMUM_CAPACITY = 1 << 30;this

​ // 默認加載因子線程

​ static final float DEFAULT_LOAD_FACTOR = 0.75f;

​ // 存儲數據的Entry數組,長度是2的冪。

​ // HashMap是採用拉鍊法實現的,每個Entry本質上是一個單向鏈表

​ transient Entry[] table;

​ // HashMap的大小,它是HashMap保存的鍵值對的數量

​ transient int size;

​ // HashMap的閾值,用於判斷是否須要調整HashMap的容量(threshold = 容量*加載因子)

​ int threshold;

​ // 加載因子實際大小

​ final float loadFactor;

​ // HashMap被改變的次數

​ transient volatile int modCount;

​ // 指定「容量大小」和「加載因子」的構造函數

public HashMap(int initialCapacity, float loadFactor) {

         if (initialCapacity < 0)

             throw new IllegalArgumentException("Illegal initial capacity: " +

                                                initialCapacity);

​ // HashMap的最大容量只能是MAXIMUM_CAPACITY

if (initialCapacity > MAXIMUM_CAPACITY)

             initialCapacity = MAXIMUM_CAPACITY;

         if (loadFactor <= 0 || Float.isNaN(loadFactor))

             throw new IllegalArgumentException("Illegal load factor: " +

                                                loadFactor);

​ // 找出「大於initialCapacity」的最小的2的冪

int capacity = 1;

         while (capacity < initialCapacity)

             capacity <<= 1;

​ // 設置「加載因子」

this.loadFactor = loadFactor;

​ // 設置「HashMap閾值」,當HashMap中存儲數據的數量達到threshold時,就須要將HashMap的容量加倍。

threshold = (int)(capacity * loadFactor);

​ // 建立Entry數組,用來保存數據

table = new Entry[capacity];

         init();

     }

​ // 指定「容量大小」的構造函數

public HashMap(int initialCapacity) {

         this(initialCapacity, DEFAULT_LOAD_FACTOR);

     }

​ // 默認構造函數。

public HashMap() {

​ // 設置「加載因子」

this.loadFactor = DEFAULT_LOAD_FACTOR;

​ // 設置「HashMap閾值」,當HashMap中存儲數據的數量達到threshold時,就須要將HashMap的容量加倍。

threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);

​ // 建立Entry數組,用來保存數據

table = new Entry[DEFAULT_INITIAL_CAPACITY];

         init();

     }

​ // 包含「子Map」的構造函數

public HashMap(Map<? extends K, ? extends V> m) {

        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,

                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);

​ // 將m中的所有元素逐個添加到HashMap中

putAllForCreate(m);

     }

     static int hash(int h) {

         h ^= (h >>> 20) ^ (h >>> 12);

         return h ^ (h >>> 7) ^ (h >>> 4);

     }

​ // 返回索引值

​ // h & (length-1)保證返回值的小於length

static int indexFor(int h, int length) {

         return h & (length-1);

     }

     public int size() {

         return size;

     }

     public boolean isEmpty() {

         return size == 0;

     }

​ // 獲取key對應的value

public V get(Object key) {

         if (key == null)

             return getForNullKey();

​ // 獲取key的hash值

int hash = hash(key.hashCode());

​ // 在「該hash值對應的鏈表」上查找「鍵值等於key」的元素

for (Entry<K,V> e = table[indexFor(hash, table.length)];

              e != null;

              e = e.next) {

             Object k;

             if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

                 return e.value;

         }

         return null;

     }

​ // 獲取「key爲null」的元素的值

​ // HashMap將「key爲null」的元素存儲在table[0]位置!

private V getForNullKey() {

         for (Entry<K,V> e = table[0]; e != null; e = e.next) {

             if (e.key == null)

                 return e.value;

         }

         return null;

     }

​ // HashMap是否包含key

public boolean containsKey(Object key) {

         return getEntry(key) != null;

     }

​ // 返回「鍵爲key」的鍵值對

final Entry<K,V> getEntry(Object key) {

​ // 獲取哈希值

​ // HashMap將「key爲null」的元素存儲在table[0]位置,「key不爲null」的則調用hash()計算哈希值

int hash = (key == null) ? 0 : hash(key.hashCode());

​ // 在「該hash值對應的鏈表」上查找「鍵值等於key」的元素

for (Entry<K,V> e = table[indexFor(hash, table.length)];

              e != null;

              e = e.next) {

             Object k;

             if (e.hash == hash &&

                 ((k = e.key) == key || (key != null && key.equals(k))))

                 return e;

         }

         return null;

     }

​ // 將「key-value」添加到HashMap中

public V put(K key, V value) {

​ // 若「key爲null」,則將該鍵值對添加到table[0]中。

if (key == null)

            return putForNullKey(value);

​ // 若「key不爲null」,則計算該key的哈希值,而後將其添加到該哈希值對應的鏈表中。

int hash = hash(key.hashCode());

         int i = indexFor(hash, table.length);

         for (Entry<K,V> e = table[i]; e != null; e = e.next) {

             Object k;

​ // 若「該key」對應的鍵值對已經存在,則用新的value取代舊的value。而後退出!

if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {

                 V oldValue = e.value;

                 e.value = value;

                 e.recordAccess(this);

                 return oldValue;

             }

         }

​ // 若「該key」對應的鍵值對不存在,則將「key-value」添加到table中

modCount++;

         addEntry(hash, key, value, i);

         return null;

     }

​ // putForNullKey()的做用是將「key爲null」鍵值對添加到table[0]位置

private V putForNullKey(V value) {

         for (Entry<K,V> e = table[0]; e != null; e = e.next) {

             if (e.key == null) {

                 V oldValue = e.value;

                 e.value = value;

                 e.recordAccess(this);

                 return oldValue;

             }

         }

​ // 這裏的徹底不會被執行到!

modCount++;

         addEntry(0, null, value, 0);

         return null;

     }

​ // 建立HashMap對應的「添加方法」,

​ // 它和put()不一樣。putForCreate()是內部方法,它被構造函數等調用,用來建立HashMap

​ // 而put()是對外提供的往HashMap中添加元素的方法。

private void putForCreate(K key, V value) {

         int hash = (key == null) ? 0 : hash(key.hashCode());

         int i = indexFor(hash, table.length);

​ // 若該HashMap表中存在「鍵值等於key」的元素,則替換該元素的value值

for (Entry<K,V> e = table[i]; e != null; e = e.next) {

             Object k;

             if (e.hash == hash &&

                 ((k = e.key) == key || (key != null && key.equals(k)))) {

                 e.value = value;

                 return;

             }

         }

​ // 若該HashMap表中不存在「鍵值等於key」的元素,則將該key-value添加到HashMap中

createEntry(hash, key, value, i);

    }

​ // 將「m」中的所有元素都添加到HashMap中。

​ // 該方法被內部的構造HashMap的方法所調用。

private void putAllForCreate(Map<? extends K, ? extends V> m) {

​ // 利用迭代器將元素逐個添加到HashMap中

for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {

             Map.Entry<? extends K, ? extends V> e = i.next();

             putForCreate(e.getKey(), e.getValue());

         }

     }

​ // 從新調整HashMap的大小,newCapacity是調整後的單位

void resize(int newCapacity) {

         Entry[] oldTable = table;

         int oldCapacity = oldTable.length;

         if (oldCapacity == MAXIMUM_CAPACITY) {

             threshold = Integer.MAX_VALUE;

             return;

         }

​ // 新建一個HashMap,將「舊HashMap」的所有元素添加到「新HashMap」中,

​ // 而後,將「新HashMap」賦值給「舊HashMap」。

Entry[] newTable = new Entry[newCapacity];

         transfer(newTable);

         table = newTable;

         threshold = (int)(newCapacity * loadFactor);

     }

​ // 將HashMap中的所有元素都添加到newTable中

void transfer(Entry[] newTable) {

         Entry[] src = table;

         int newCapacity = newTable.length;

         for (int j = 0; j < src.length; j++) {

             Entry<K,V> e = src[j];

             if (e != null) {

                 src[j] = null;

                 do {

                     Entry<K,V> next = e.next;

                     int i = indexFor(e.hash, newCapacity);

                     e.next = newTable[i];

                     newTable[i] = e;

                     e = next;

                 } while (e != null);

             }

         }

     }

​ // 將"m"的所有元素都添加到HashMap中

public void putAll(Map<? extends K, ? extends V> m) {

​ // 有效性判斷

int numKeysToBeAdded = m.size();

         if (numKeysToBeAdded == 0)

             return;

​ // 計算容量是否足夠,

​ // 若「當前實際容量 < 須要的容量」,則將容量x2。

if (numKeysToBeAdded > threshold) {

             int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);

             if (targetCapacity > MAXIMUM_CAPACITY)

                 targetCapacity = MAXIMUM_CAPACITY;

             int newCapacity = table.length;

             while (newCapacity < targetCapacity)

                 newCapacity <<= 1;

             if (newCapacity > table.length)

                 resize(newCapacity);

         }

​ // 經過迭代器,將「m」中的元素逐個添加到HashMap中。

for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext(); ) {

             Map.Entry<? extends K, ? extends V> e = i.next();

             put(e.getKey(), e.getValue());

         }

     }

​ // 刪除「鍵爲key」元素

public V remove(Object key) {

         Entry<K,V> e = removeEntryForKey(key);

         return (e == null ? null : e.value);

     }

​ // 刪除「鍵爲key」的元素

final Entry<K,V> removeEntryForKey(Object key) {

​ // 獲取哈希值。若key爲null,則哈希值爲0;不然調用hash()進行計算

int hash = (key == null) ? 0 : hash(key.hashCode());

         int i = indexFor(hash, table.length);

         Entry<K,V> prev = table[i];

         Entry<K,V> e = prev;

​ // 刪除鏈表中「鍵爲key」的元素

​ // 本質是「刪除單向鏈表中的節點」

while (e != null) {

            Entry<K,V> next = e.next;

            Object k;

            if (e.hash == hash &&

                ((k = e.key) == key || (key != null && key.equals(k)))) {

                modCount++;

                size--;

                if (prev == e)

                    table[i] = next;

                else

                    prev.next = next;

                e.recordRemoval(this);

                return e;

            }

            prev = e;

            e = next;

        }

        return e;

    }

​ // 刪除「鍵值對」

final Entry<K,V> removeMapping(Object o) {

         if (!(o instanceof Map.Entry))

             return null;

         Map.Entry<K,V> entry = (Map.Entry<K,V>) o;

         Object key = entry.getKey();

         int hash = (key == null) ? 0 : hash(key.hashCode());

         int i = indexFor(hash, table.length);

         Entry<K,V> prev = table[i];

         Entry<K,V> e = prev;
相關文章
相關標籤/搜索