国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

HashMap

2019-11-14 21:44:44
字體:
來源:轉載
供稿:網友
HashMapHashMap

HashMap是基于哈希表的Map接口的非同步實現。允許使用null值和null鍵。

數據結構

11

HashMap是一個“鏈表散列”的數據結構,即數組和鏈表的結合體。HashMap底層就是一個數組結構,數組中的每一項又是一個鏈表。當新建一個HashMap的時候,就會初始化一個數組。

/**  * The table, resized as necessary. Length MUST Always be a power of two.  */  transient Entry[] table;    static class Entry<K,V> implements Map.Entry<K,V> {      final K key;      V value;      Entry<K,V> next;      final int hash;      &hellip;…  }

Entry就是數組中的元素,每個 Map.Entry 其實就是一個key-value對,它持有一個指向下一個元素的引用,這就構成了鏈表。

存取實現
public V put(K key, V value) {      // HashMap允許存放null鍵和null值。      // 當key為null時,調用putForNullKey方法,將value放置在數組第一個位置。      if (key == null)          return putForNullKey(value);      // 根據key的keyCode重新計算hash值。      int hash = hash(key.hashCode());      // 搜索指定hash值在對應table中的索引。      int i = indexFor(hash, table.length);      // 如果 i 索引處的 Entry 不為 null,通過循環不斷遍歷 e 元素的下一個元素。      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))) {              V oldValue = e.value;              e.value = value;              e.recordaccess(this);              return oldValue;          }      }      // 如果i索引處的Entry為null,表明此處還沒有Entry。      modCount++;      // 將key、value添加到i索引處。      addEntry(hash, key, value, i);      return null;  }

當我們往HashMap中put元素的時候,先根據key的hashCode重新計算hash值,根據hash值得到這個元素在數組中的位置(即下標),如果數組該位置上已經存放有其他元素了,那么在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最先加入的放在鏈尾。如果數組該位置上沒有元素,就直接將該元素放到此數組中的該位置上。

addEntry(hash, key, value, i)方法根據計算出的hash值,將key-value對放在數組table的i索引處。addEntry 是HashMap 提供的一個包訪問權限的方法,代碼如下:

void addEntry(int hash, K key, V value, int bucketIndex) {      // 獲取指定 bucketIndex 索引處的 Entry       Entry<K,V> e = table[bucketIndex];      // 將新創建的 Entry 放入 bucketIndex 索引處,并讓新的 Entry 指向原來的 Entry      table[bucketIndex] = new Entry<K,V>(hash, key, value, e);      // 如果 Map 中的 key-value 對的數量超過了極限      if (size++ >= threshold)      // 把 table 對象的長度擴充到原來的2倍。          resize(2 * table.length);  }

hash(int h)方法根據key的hashCode重新計算一次散列。此算法加入了高位計算,防止低位不變,高位變化時,造成的hash沖突。

static int hash(int h) {      h ^= (h >>> 20) ^ (h >>> 12);      return h ^ (h >>> 7) ^ (h >>> 4);  }

對于任意給定的對象,只要它的 hashCode() 返回值相同,那么程序調用 hash(int h) 方法所計算得到的 hash 碼值總是相同的。我們首先想到的就是把hash值對數組長度取模運算,這樣一來,元素的分布相對來說是比較均勻的。但是,“模”運算的消耗還是比較大的,在HashMap中是這樣做的:調用 indexFor(int h, int length) 方法來計算該對象應該保存在 table 數組的哪個索引處。indexFor(int h, int length) 方法的代碼如下:

static int indexFor(int h, int length) {      return h & (length-1);  }

它通過 h & (table.length -1) 來得到該對象的保存位,而HashMap底層數組的長度總是 2 的n 次方,這是HashMap在速度上的優化。

int capacity = 1;      while (capacity < initialCapacity)          capacity <<= 1;

這段代碼保證初始化時HashMap的容量總是2的n次方,即底層數組的長度總是為2的n次方。

當length總是 2 的n次方時,h& (length-1)運算等價于對length取模,也就是h%length,但是&比%具有更高的效率。

public V get(Object key) {      if (key == null)          return getForNullKey();      int hash = hash(key.hashCode());      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;  }

從HashMap中get元素時,首先計算key的hashCode,找到數組中對應位置的某一元素,然后通過key的equals方法在對應位置的鏈表中找到需要的元素。

歸納起來簡單地說,HashMap 在底層將 key-value 當成一個整體進行處理,這個整體就是一個 Entry 對象。HashMap 底層采用一個 Entry[] 數組來保存所有的 key-value 對,當需要存儲一個 Entry 對象時,會根據hash算法來決定其在數組中的存儲位置,在根據equals方法決定其在該數組位置上的鏈表中的存儲位置;當需要取出一個Entry時,也會根據hash算法找到其在數組中的存儲位置,再根據equals方法從該位置上的鏈表中取出該Entry。

Fail-Fast機制

我們知道java.util.HashMap不是線程安全的,因此如果在使用迭代器的過程中有其他線程修改了map,那么將拋出ConcurrentModificationException,這就是所謂fail-fast策略。

這一策略在源碼中的實現是通過modCount域,modCount顧名思義就是修改次數,對HashMap內容的修改都將增加這個值,那么在迭代器初始化過程中會將這個值賦給迭代器的expectedModCount。

HashIterator() {      expectedModCount = modCount;      if (size > 0) { // advance to first entry      Entry[] t = table;      while (index < t.length && (next = t[index++]) == null)          ;      }  }

在迭代過程中,判斷modCount跟expectedModCount是否相等,如果不相等就表示已經有其他線程修改了Map:

注意到modCount聲明為volatile,保證線程之間修改的可見性。

final Entry<K,V> nextEntry() {         if (modCount != expectedModCount)             throw new ConcurrentModificationException();

迭代器的快速失敗行為不能得到保證,一般來說,存在非同步的并發修改時,不可能作出任何堅決的保證。快速失敗迭代器盡最大努力拋出 ConcurrentModificationException。因此,編寫依賴于此異常的程序的做法是錯誤的,正確做法是:迭代器的快速失敗行為應該

僅用于檢測程序錯誤。

我是天王蓋地虎的分割線
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 分宜县| 祁门县| 中阳县| 兴和县| 永春县| 扬州市| 龙陵县| 郸城县| 万载县| 广安市| 桃江县| 郑州市| 景洪市| 茂名市| 宁明县| 海丰县| 任丘市| 木里| 桂东县| 洱源县| 桦甸市| 永仁县| 陵川县| 平湖市| 尼木县| 凯里市| 祥云县| 清镇市| 五原县| 苗栗县| 延川县| 镇赉县| 门头沟区| 柳江县| 渑池县| 宁海县| 巫溪县| 山阳县| 册亨县| 苍山县| 平舆县|