HashMap的初始化
int DEFAULT_INITIAL_CAPACITY = 16:默認的初始容量為16
int MAXIMUM_CAPACITY = 1 << 30:最大的容量為 2 ^ 30
float DEFAULT_LOAD_FACTOR = 0.75f:默認的加載因子為 0.75f
Entry< K,V>[] table:Entry類型的數(shù)組,HashMap用這個來維護內(nèi)部的數(shù)據(jù)結(jié)構(gòu),它的長度由容量決定
int size:HashMap的大小
int threshold:HashMap的極限容量,擴容臨界點(容量和加載因子的乘積)
每次新建一個HashMap時,都會初始化一個table數(shù)組。table數(shù)組的元素為Entry節(jié)點

HashMap如何解決沖突
背景
散列表要解決的一個問題就是散列值的沖突問題,通常是兩種方法:鏈表法和開放地址法。
鏈表法就是將相同hash值的對象組織成一個鏈表放在hash值對應的槽位;
開放地址法是通過一個探測算法,當某個槽位已經(jīng)被占據(jù)的情況下繼續(xù)查找下一個可以使用的槽位
HashMap里面沒有出現(xiàn)hash沖突時,沒有形成單鏈表時,hashmap查找元素很快,get()方法能夠直接定位到元素,但是出現(xiàn)單鏈表后,單個bucket 里存儲的不是一個 Entry,而是一個 Entry 鏈,系統(tǒng)只能必須按順序遍歷每個 Entry,直到找到想搜索的 Entry 為止——如果恰好要搜索的 Entry 位于該 Entry 鏈的最末端(該 Entry 是最早放入該 bucket 中),那系統(tǒng)必須循環(huán)到最后才能找到該元素.
創(chuàng)建 HashMap 時,有一個默認的負載因子(load factor),其默認值為 0.75,這是時間和空間成本上一種折衷:增大負載因子可以減少 Hash 表(就是那個 Entry 數(shù)組)所占用的內(nèi)存空間,但會增加查詢數(shù)據(jù)的時間開銷,而查詢是最頻繁的的操作(HashMap 的 get() 與 put() 方法都要用到查詢);減小負載因子會提高數(shù)據(jù)查詢的性能,但會增加 Hash 表所占用的內(nèi)存空間
線程不安全的HashMap
因為多線程環(huán)境下,使用Hashmap進行put操作會引起死循環(huán),導致CPU利用率接近100%,所以在并發(fā)情況下不能使用HashMap
final HashMap<String, String> map = new HashMap<String, String>(2);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
new Thread(new Runnable() {
@Override
public void run() {
map.put(UUID.randomUUID().toString(), "");
}
}, "ftf" + i).start();
}
}
}, "ftf");
t.start();
t.join();
效率低下的HashTable容器
HashTable容器使用synchronized來保證線程安全,但在線程競爭激烈的情況下HashTable的效率非常低下。因為當一個線程訪問HashTable的同步方法時,其他線程訪問HashTable的同步方法時,可能會進入阻塞或輪詢狀態(tài)。如線程1使用put進行添加元素,線程2不但不能使用put方法添加元素,并且也不能使用get方法來獲取元素,所以競爭越激烈效率越低。
ConcurrentHashMap的鎖分段技術(shù)
HashTable容器在競爭激烈的并發(fā)環(huán)境下表現(xiàn)出效率低下的原因,是因為所有訪問HashTable的線程都必須競爭同一把鎖,那假如容器里有多把鎖,每一把鎖用于鎖容器其中一部分數(shù)據(jù),那么當多線程訪問容器里不同數(shù)據(jù)段的數(shù)據(jù)時,線程間就不會存在鎖競爭,從而可以有效的提高并發(fā)訪問效率,這就是ConcurrentHashMap所使用的鎖分段技術(shù),首先將數(shù)據(jù)分成一段一段的存儲,然后給每一段數(shù)據(jù)配一把鎖,當一個線程占用鎖訪問其中一個段數(shù)據(jù)的時候,其他段的數(shù)據(jù)也能被其他線程訪問。