HashMap,HashTabel,ConcurrentHashMap

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é)點

image.png

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ù)也能被其他線程訪問。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • Java8張圖 11、字符串不變性 12、equals()方法、hashCode()方法的區(qū)別 13、...
    Miley_MOJIE閱讀 3,896評論 0 11
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,706評論 18 399
  • Java SE 基礎(chǔ): 封裝、繼承、多態(tài) 封裝: 概念:就是把對象的屬性和操作(或服務)結(jié)合為一個獨立的整體,并盡...
    Jayden_Cao閱讀 2,247評論 0 8
  • java筆記第一天 == 和 equals ==比較的比較的是兩個變量的值是否相等,對于引用型變量表示的是兩個變量...
    jmychou閱讀 1,648評論 0 3
  • 尊重原創(chuàng):(口訣)轉(zhuǎn)自http://lasombra.iteye.com/blog/991662 單目乘除為關(guān)系,...
    ifeelok0319閱讀 435評論 0 0

友情鏈接更多精彩內(nèi)容