關於HashMap,你應該瞭解這些

HashMap是非常重要的數據結構,並且大部分面試都會問到,優秀的java程序員應當要對HashMap進行深入的瞭解,今天我們就來剖析一下它。

目錄

  • HashMap簡介
  • 成員變量
  • get和put的流程
  • hashMap相關的面試題
  • 總結

一.簡介

首先,HashMap是一個無序key,value集合,它的底層存儲是由數組加鏈表和紅黑樹結構組成的的。在進行添加,刪除和查找時,效率非常高,如果不考慮哈希碰撞,一次定位就能完成操作,時間複雜度爲O(1)。
下面是一個默認長度的hashMap。

在這裏插入圖片描述

二.hashMap的成員變量

1.初始大小

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

這個是值是數組長度,也就是數組的初始最大行數,當然如果碰撞比較多,也有可能hashMap存了200個值,但是容量還是16。當然這不是理想狀態。

2.最大值

static final int MAXIMUM_CAPACITY = 1 << 30;

這個沒有什麼好說的,hashMap最大容量。

3.增長因子

static final float DEFAULT_LOAD_FACTOR = 0.75f;

增長因子,就是如果數組被佔用的大小超過當前大小的75%時,就進行擴容,按照當前容量二倍創建一個新的Entry,把舊Entry裏的值重新hash計算,放到新的Entry裏。

4.變樹閾值

static final int TREEIFY_THRESHOLD = 8;

jdk8對hashMap做的優化,當鏈表長度大於8時,鏈表轉換成紅黑樹,因爲紅黑樹的查詢效率是O(logn),
而鏈表的查詢效率是O(n)

5.變鏈閾值

static final int UNTREEIFY_THRESHOLD = 6;

當紅黑樹總元素數小於6時,再轉換成鏈表。因爲紅黑樹元素太少時,效率比較低,所以轉換成鏈表

三.HashMap的構造器

HashMap 提供了四個構造器,下面來一一看下。

1.HashMap()

使用默認的構造函數,構造一個初始容量爲16,增長因子爲0.75的空HashMap;

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
static final float DEFAULT_LOAD_FACTOR = 0.75f;

public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; 
}

2.HashMap(int initialCapacity, float loadFactor)

第一個參數是初始大小,第二個參數是增長因子,通過這兩個參數來構造一個空的HashMap。

public HashMap(int initialCapacity, float loadFactor) {
    //判斷容量是否小於0
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    //判斷容量是否超過最大值
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    //判斷加載因子
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    this.loadFactor = loadFactor;
    //計算下一次resize的閾值
    this.threshold = tableSizeFor(initialCapacity);
}

3.HashMap(int initialCapacity)

一個參數的構造,通過初始增長因子構造一個空的HashMap,調用

HashMap(int initialCapacity, float loadFactor)構造方法。
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

4.HashMap(Map<? extends K, ? extends V> m)

根據已有的Map接口創建一個元素相同的hashMap,使用默認的初始容量和增長因子。
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}

四.get和put方法

1.put方法

在HashMap插入值時,會判斷是否已經初始化,如果沒有初始化,就會創建一個長度爲16的Entry,設置增長因子,默認爲0.75。

然後判斷key是否是Null,如果是Null則放到0處,如果不是,則對其key進行hash,實際操作是高位與低位混淆後做求餘操作,然後,如果hash求出來的值與數組上的key值進行匹配,如果沒有匹配上,則放到對應的數組bucket位置;如果與之前可以匹配上了(即爲hash碰撞),則一一對比鏈表上的Key值是否相同,如果匹配上了則對當前位置的值進行修改,如果沒有匹配的key,則放到該鏈表的尾部。

2.get方法

在hashMap進行get操作時,先判斷key是否是null,如果是null則,直接取0處的bucket值,
如果不是則對key的hash值和數組上的每一個bucket的key的hash值對比沒如果相同,則循環鏈表並對比key值是否相同,如果都相同,則返回該位置的value,如果沒有相同的,則返回空。

五.HashMap相關的面試題

1.hashMap和hashtable區別
hashMap和Hashtable的內部結構基本一致。
1)hashMap支持null 作爲key,但hashtable不支持;
2)HashMap是非線程安全的數據結構,所以相對來說,存取速度比較快;而Hashtable是線程安全的,因爲hashtable的get、put等方法上添加了synchrounized的參數,對大部分方法上了排他鎖,也就是說,在一個線程訪問時其他線程都要等待。

但是如果數據量增大以後,HashTable的效率下降的比較快,所以如果你需要保證線程安全又要兼顧效率的話,從jdk1.6版本開始,java給提供了ConcurrentHashMap,它相對於hashtable的性能,做了優化,

它提出了分段鎖機制, 它默認分8個段,操作開始時,先判斷在那段內,然後操作時只鎖定該段的數據,這樣的話,只有訪問同一個段的線程纔回排隊等待,訪問不同段的線程之間互不衝突,這樣來說,相對於hashtable對多線程的操作的效率提升了至少8倍,多線程併發操作效率得到了極大的提高。

2.Map<String,Object> map = new HashMap<>(20),問,該HashMap擴容了幾次?

答:擴容了0次,因爲它調用了HashMap的初始化方法,直接創建了一個長度爲32的HashMap,而不是一個一個添加值,添加了20次,所以它只擴容了1次。

3.Map<String,Object> map = new HashMap<>(28),問,這條語句java虛擬機做了什麼?

答:該語句創建了一個初始容量爲64的空格的HashMap,爲什麼是64而不是32呢,因爲它沒有傳加載因子,所以他的加載因子是0.75,28/32=0.875 ,已超過了0.75的閾值,所以自動擴容爲當前的二倍,創建了一個長度爲64的hashMap。

總結

今天對HashMap的實現原理進行了講解,希望本篇文章對大家的學習有所幫助,同時也歡迎評論指正,謝謝支持!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章