HashMap的內部實現機制
1.HashMap的內部實現機制
HashMap是對資料結構中雜湊表(Hash Table)的實現, Hash表又叫雜湊表。Hash表是根據關鍵碼Key來訪問其對應的值Value的資料結構,它通過一個對映函式把關鍵碼對映到表中一個位置來訪問該位置的值,從而加快查詢的速度。這個對映函式叫做Hash函式,存放記錄的陣列叫做Hash表。
在Java中,HashMap的內部實現結合了連結串列和陣列的優勢,連結節點的資料結構是 Entry<k,v>,每個Entry物件的內部又含有指向下一個Entry型別物件的引用,如以下程式碼所示:
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
Entry<K,V> next; //Entry型別內部有一個自己型別的引用,指向下一個Entry
final int hash;
...
}
在HashMap的建構函式中可以看到,Entry表被申明為了陣列,如以下程式碼所示:
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
在以上建構函式中, 預設的 DEFAULT_INITIAL_CAPACITY值為16,DEFAULT_LOAD_FACTOR的值為0.75。
當put一個元素到HashMap中去時,其內部實現如下:
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
...
}
可以看到put函式中用一個hash函式來得到雜湊值,需要指出的是,HashTable在實現時直接用了hashCode作為雜湊值,因此採用HashMap代替HashTable有一定的優化。
put函式中用到的兩個函式hash和indexFor其實現分別如下:
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
至於hash函式為什麼這樣設計,這涉及到具體雜湊函式的設計問題了,需要考慮的是雜湊演算法的時間複雜度,同時儘量使得陣列上每個位置都有值,求得時間和空間的最優。
indexFor函式則用了一個很巧妙的與運算將index值限制在了length-1之內。
當然,hash函式存在衝突的情況,同一個key對應的hash值可能相同,這時候hash值相同的元素就會用連結進行儲存,HashMap的get方法在獲取value的時候會對連結串列進行遍歷,把key值相匹配的value取出來。
2.Hash的實現
主要是雜湊演算法和衝突的解決。
3.什麼時候ReHash
在介紹HashMap的內部實現機制時提到了兩個引數,DEFAULT_INITIAL_CAPACITY和DEFAULT_LOAD_FACTOR,DEFAULT_INITIAL_CAPACITY是table陣列的容量,DEFAULT_LOAD_FACTOR則是為了最大程度避免雜湊衝突,提高HashMap效率而設定的一個影響因子,將其乘以DEFAULT_INITIAL_CAPACITY就得到了一個閾值threshold,當HashMap的容量達到threshold時就需要進行擴容,這個時候就要進行ReHash操作了,可以看到下面addEntry函式的實現,當size達到threshold時會呼叫resize函式進行擴容。
void addEntry(int hash, K key, V value, int bucketIndex) {
ntry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
if (size++ >= threshold)
resize(2 * table.length);
}
在擴容的過程中需要進行ReHash操作,而這是非常耗時的,在實際中應該儘量避免。
(原創文章,轉載請註明作者schbook:seekerxu@163.com)
相關文章
- HBase內部機制
- redis 內部機制Redis
- 模擬實現和深入理解Node Stream內部機制
- Redis處理客戶端連線的內部實現機制RXRedis客戶端
- 從原始碼的角度來談一談HashMap的內部實現原理原始碼HashMap
- WPF原始碼分析系列一:剖析WPF模板機制的內部實現(一)原始碼
- WPF原始碼分析系列一:剖析WPF模板機制的內部實現(五)原始碼
- gostring的內部實現Go
- JavaScript 深入解剖bind內部機制JavaScript
- 探一探現代瀏覽器的內部機制(一)瀏覽器
- mysqldump的內部實現原理MySql
- ElasticSearch 文件(document)內部機制詳解Elasticsearch
- Angular 中攔截器的真相和 HttpClient 內部機制AngularHTTPclient
- js內部事件機制–單執行緒原理JS事件執行緒
- Spring AOP 的實現機制Spring
- HashMap的實現原理 HashMap底層實現,hashCode如何對應bucket?HashMap
- Java HashMap原理及內部儲存結構JavaHashMap
- 簡單的 HashMap 實現HashMap
- HashMap擴容機制原始碼分析HashMap原始碼
- Object.create(..)和new(..)的內部實現Object
- kafka的內部實現、安裝和使用Kafka
- 精讀《JS 陣列的內部實現》JS陣列
- 非酋的福音?談一談遊戲內的偽隨機機制以及實現遊戲隨機
- 通過WordCount解析Spark RDD內部原始碼機制Spark原始碼
- ElasticSearch內部基於_version樂觀鎖控制機制Elasticsearch
- MySQL中的MVCC實現機制MySqlMVC
- docker 實現 Redis 的哨兵機制DockerRedis
- Js非同步機制的實現JS非同步
- HashMap的底層結構、原理、擴容機制HashMap
- 探索計算機內部的神秘語言:二進位制的魅力計算機
- Java中HashMap的實現原理JavaHashMap
- Java 阻塞佇列(BlockingQueue)的內部實現原理Java佇列BloC
- 浮點數演算法的內部實現演算法
- 併發機制的底層實現
- Nginx accept鎖的機制和實現Nginx
- [轉載]Spring AOP的實現機制Spring
- 【譯】Go 切片:用法和內部實現Go
- 遊戲的藝術修養:加速通脹的內部經濟機制遊戲
- 智慧合約從入門到精通:Solidity的特性與內部機制Solid