HashMap的數據結構:
數組+鏈表,數組中元素是個鏈表,存儲Key的hashcode碰撞的元素
其中元素的節點為:
static class Node<K,V> implements Map.Entry<K,V> { 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; } }
每個Node含有指向下一個Node的指針
數組(HashMap大小)的初始長度16
/** * The default initial capacity - MUST be a power of two. */ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
數組的增長因子,0.75
/** * The load factor used when none specified in constructor. */ static final float DEFAULT_LOAD_FACTOR = 0.75f;
HashMap的實現重點需要注意的在兩個方面,一個是鏈表結構,一個是table的resize()
HashMap處理hashcode碰撞的方式用鏈表,hashcode相同的元素頭尾相連組成一個單鏈,並把最開始的那個節點存儲在數組中,訪問的時候,先通過hash(key)找到數組下標,再迭代單鏈找到equals()的value,然后返回
resize的時候,如果當前數組的占用率達到負載因子0.75,則會觸發一次resize(),增長量為原來容量(table.length)的一倍,newCap = oldCap << 1
然后把老數組的數據遷移到新數組
//=============================================================分割線====================================================================
HashMap和HashTable的區別:
他們的結構差不多,只不過HashTable是線程安全的,HashTable是所有暴露的操作都加鎖,synchronized,這種情況下性能比較低,容易引起活躍性問題
HashTable跟java.util.Collections#synchronizedMap很接近
HashMap允許key和value為null
HashTable不允許key和value為null
ConcurrentHashMap也是線程安全的,是采用CAS的方式來處理並發操作,如果單鏈比較長就坍縮為一個紅黑樹,logn的時間復雜度
ConcurrentHashMap要分jdk1.8之前還是之后
1.8之前的ConcurrentHashMap是采用分段(Segment)的方式,加鎖時直接在Segment上加鎖,縮小了加鎖范圍,提高了性能
1.8之后的ConcurrentHashMap是重寫的,加鎖范圍進一步縮小,采用CAS將加鎖范圍縮小到單個數組元素
性能上ConcurrentHashMap比前面的要高