HashMap

參考:http://www.cnblogs.com/joemsu/p/7724623.htmlgit

數據結構

jdk1.8:數組、鏈表/紅黑樹(jdk1.7的是數組+鏈表)github

節點數據類型:
Node<K,V>(hash,key,value,next)數組

static class Node<K,V> implements Map.Entry<K,V> {
  // key & value 的 hash值
  final int hash;
  final K key;
  V value;
  //指向下一個節點
  Node<K,V> next;

  Node(int hash, K key, V value, Node<K,V> next) {
    this.hash = hash;
    this.key = key;
    this.value = value;
    this.next = next;
  }

  public final K getKey()        { return key; }
  public final V getValue()      { return value; }
  public final String toString() { return key + "=" + value; }

  public final int hashCode() {
    return Objects.hashCode(key) ^ Objects.hashCode(value);
  }

  public final V setValue(V newValue) {
    V oldValue = value;
    value = newValue;
    return oldValue;
  }

  public final boolean equals(Object o) {
    if (o == this)
      return true;
    if (o instanceof Map.Entry) {
      Map.Entry<?,?> e = (Map.Entry<?,?>)o;
      if (Objects.equals(key, e.getKey()) &&
          Objects.equals(value, e.getValue()))
        return true;
    }
    return false;
  }
}

參數

/默認初始化map的容量:16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//map的最大容量:2^30
static final int MAXIMUM_CAPACITY = 1 << 30;
//默認的填充因子:0.75,能較好的平衡時間與空間的消耗
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//將鏈表(桶)轉化成紅黑樹的臨界值
static final int TREEIFY_THRESHOLD = 8;
//將紅黑樹轉成鏈表(桶)的臨界值
static final int UNTREEIFY_THRESHOLD = 6;
//轉變成樹的table的最小容量,小於該值則不會進行樹化
static final int MIN_TREEIFY_CAPACITY = 64;
//上圖所示的數組,長度老是2的冪次
transient Node<K,V>[] table;
//map中的鍵值對集合
transient Set<Map.Entry<K,V>> entrySet;
//map中鍵值對的數量
transient int size;
//用於統計map修改次數的計數器,用於fail-fast拋出ConcurrentModificationException
transient int modCount;
//大於該閾值,則從新進行擴容,threshold = capacity(table.length) * load factor
int threshold;
//填充因子
final float loadFactor;

重要方法

get

根據key的hashCode計算在數組裏的下標,而後再判斷該位置是否符合條件,不然根據鏈表/紅黑樹的方法繼續找安全

計算下標的方法:
下標 = (n-1) & hash
hash = (h = key.hashCode()) & h>>>16數據結構

判斷兩個key是否相同:性能

if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))

先判斷hash,若是hash不一樣必定不一樣;若是hash相同,則利用equals判斷是否相同this

put

先檢查容量(擴容resize)。
根據hashCode計算桶在數組裏的位置,
插入該處的鏈表/樹線程

resize

兩倍容量。若是容量已達最大值,則設置容量爲int_max。
擴容以後要從新計算index,每一個桶裏的節點都要從新計算位置,計算位置的方法是hash & (n-1)。
擴容以後因爲newCap-1比oldCap-1多了一個高位(例如8-1=7=111,而4-1=3=11),所以節點的新位置取決於多出來的一個高位bit,若是該位是0,則仍是原位置;不然應該是原位置+舊容量code

擴容的過程對整個數組和桶進行了遍歷,耗費性能,因此最好在建立HashMap的時候指定容量,不然在第一次put的時候會進行一次擴容(由於table==null)

HashMap與HashTable的區別

  • 線程安全性:Hash Table線程安全,HashMap線程不安全
  • null鍵值的支持:HashMap最多有一個null鍵,null值不作約束;HashTable不接受null鍵值
  • 初始容量及擴容:HashTable初始容量11,擴容2n+1;HashMap初始容量16,擴容2n(即容量時鐘2的冪)
  • 數據結構:jdk8之後HashMap加入了紅黑樹提升鏈表過長時的查詢速度,HashTable沒有

參考:https://yikun.github.io/2015/04/01/Java-HashMap%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/

相關文章
相關標籤/搜索