hashMap原始碼閱讀筆記1.7
阿新 • • 發佈:2018-12-13
這幾天一直在看hashMap的原始碼,也借鑑了很多大佬的文章以便更好的理解,也從大佬文章中借鑑了很多的內容,如果侵權,請告知,我將立刻刪除。
hashMap繼承AbstractMap,實現了map介面。
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable
初始容量:雜湊表建立時的容量,實際上就是Entry<k,v>[] table的容量
static final int DEFAULT_INITIAL_CAPACITY = 16;
最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
載入因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
雜湊表
transient Entry<K,V>[] table;
存在對映關係的條數
transient int size;
閾值
int threshold;
修改計數
transient int modCount;
hashMap的幾個預設構造方法:
(1):構造了一個對映關係與指定map相同的hashMap,類似拷貝
public HashMap(Map<? extends K, ? extends V> m)
(2):指定初始容量和載入因子
public HashMap(int initialCapacity, float loadFactor)
(3):指定初始容量
public HashMap(int initialCapacity)
(4):預設構造方法
public HashMap()
計算hash值
final int hash(Object k) { int h = 0; if (useAltHashing) { if (k instanceof String) { return sun.misc.Hashing.stringHash32((String) k); } h = hashSeed; } h ^= k.hashCode(); h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); }
返回索引下標
//1:h& (length-1)運算等價於對length取模,也就是h%length,但是&比%具有更高的效
//2:capacity 必須為2的n次冪,則length-1肯定為奇數,在位運算h & (length -1)唯一性更高,
//減少collision的發生,也就是保證bucketIndex低重複性
static int indexFor(int h, int length) {
return h & (length-1);
}
當前map的對映關係數目
public int size() { return size; }
判斷map是否為空,即對映條數是否為0
public boolean isEmpty() {
return size == 0;
}
0.單項鍊表的實現
// 實現了單向連結串列
static class Entry<K, V> implements Map.Entry<K, V> {
final K key;
V value;
Entry<K, V> next;
int hash;
//Map.Entry儲存一個鍵值對 和這個鍵值對持有指向下一個鍵值對的引用,如此就構成了連結串列了。
Entry(int h, K k, V v, Entry<K, V> n) {
value = v;
next = n;
key = k;
hash = h;
}
}
1.get方法
當呼叫get方法,會首先判斷key值是否為null,如果為null,呼叫getForNullKey()方法,這也是hashMap允許key值為null的原因
public V get(Object key) {
if (key == null){
return getForNullKey();
}
Entry<K,V> entry = getEntry(key);
return null == entry ? null : entry.getValue();
}
當get(null)呼叫該方法,如果存在key為null情況,就返回key為null的value值,否則,直接返回null
private V getForNullKey() {
for (Entry<K, V> e = table[0]; e != null; e = e.next) {
if (e.key == null){
return e.value;
}
}
return null;
}
當get(!=null)呼叫此方法
final Entry<K,V> getEntry(Object key) {
1.根據key計算hash值
int hash = (key == null) ? 0 : hash(key);
2.通過hash值找出entry
// 1 indexFor(hash, table.length):獲取bucketIndex
// 2 遍歷具體bucket[indexFor(hash, table.length)]下的單向連結串列
// 3 如果這個key的hashCode一樣同時key值一樣,或者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;
}
3.雜湊表中不存在key的時候返回null:例如:containsKey方法
return null;
}
2.put方法
public V put(K key, V value) {
if (key == null)
//當key為null時,呼叫putForNullKey方法
return putForNullKey(value);
//計算hash值
int hash = hash(key);
//獲取
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//如果執行put操作的時候發現key已存在,就更新value
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
//增加修改次數
//1:如果多個執行緒訪問,當發現modCount不一致,就會導致ConcurrentModificationException
//2:遍歷的時候執行remove操作也會導致ConcurrentModificationException
//可以使用iterator解決2問題,1問題也說明hashMap是執行緒不安全
//使hashMap執行緒安全:Map map = Collections.synchronizeMap(hashMap);
modCount++;
//不存在就新增
addEntry(hash, key, value, i);
return null;
}
void addEntry(int hash, K key, V value, int bucketIndex) {
//如果對映關係的數量已經超過了閾值,並且table的桶索引的值不為null
if ((size >= threshold) && (null != table[bucketIndex])) {
//擴大一倍
resize(2 * table.length);
//重新計算hash值:結構已經改變
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) {
//斷開連結串列,add結點,注意總是從連結串列的表頭處插入新結點
Entry<K,V> e = table[bucketIndex];
table[bucketIndex] = new Entry<>(hash, key, value, e);
size++;
}