對比HashTabe,HashMap,TreeMap有什麼不同,以及底層原理

Map是廣義java集合框架中的另外一部分,HaspMap作爲框架中使用頻率最高類型之一,我們有必要去深入瞭解它。

對比 HashMap,HashTable,TreeMap

相同點

  • HashMap,HashTable,TreeMap都是一些常見的map實現,是以鍵值對的形式存儲和操作數據的容器類型。

不同點

  • HashTable是早期的Java類庫提供的的一個哈希表實現,本身是同步的,不支持null鍵和空值,由於同步導致的性能開銷,使用相當較少。
  • HashMap是應用更加廣泛的哈希表實現,行爲上大致和haspTable大致相同,主要區別在於HashMap不是同步的,支持null鍵和空值。所以性能上更加優秀。
  • TreeMap是基於紅黑樹的一種提供順序訪問的Map,和HashMap不同,他的get,put,remove之類的操作都是O(log(n))的時間複雜度,具體順序可以由指定的Comparator來決定,或者根據鍵的自然順序來判斷。

HashMap常見問題

haspMap明確聲明不是線程安全的的數據結構,所以haspMap在併發環境可能出現無限循環佔用cpu,size不準確的詭異問題。

知識擴展

map結構

hashTable和hashMap在類結構上由明顯不同

hashMap等其他map實現都hi擴展abstractMap,裏面包含了通用的抽象方法,HashMap的性能表現非常依賴於哈希碼的有效性。

  • equals相等,hashcode一定要相等。
  • 重寫了hashCode也要重寫equals。
  • hashCode需要保持一致性,狀態改變的哈希值仍然要一致。
  • equals的對稱,反射,傳遞。

LinkedHashMap 和 TreeMap 都可以保證某種順序,但二者還是很不同的

  • linkedHashMap通常提供的是遍歷順序符合插入順序,他的實現是通過條目維護一個雙向鏈表。

  • 對於TreeMap他的整體順序是由鍵的順序關係決定的,通過compartoar來決定。
    HashMap源碼分析

  • HashMap 內部實現基本點分析。

  • 容量(capacity)和負載係數(load factor)。

  • 樹化
    HashMap的內部結構,可以看作是數組和鏈表組成的複合結構,數組被分成一個個桶數組,通過哈希值在這個桶數組尋址,哈希值相同的鍵值對,則以鏈表形式存儲。
    在這裏插入圖片描述
    從非拷貝構造函數的實現來看,這個表格(數組)似乎並沒有在最初就初始化好,僅僅設置了一些初始值而已。

public HashMap(int initialCapacity, float loadFactor){  
    // ... 
    this.loadFactor = loadFactor;
    this.threshold = tableSizeFor(initialCapacity);
}

然後看put方法實現

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

put裏面調用了putVal方法

final V putVal(int hash, K key, V value, boolean onlyIfAbent,
               boolean evit) {
    Node<K,V>[] tab; Node<K,V> p; int , i;
    if ((tab = table) == null || (n = tab.length) = 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == ull)
        tab[i] = newNode(hash, key, value, nll);
    else {
        // ...
        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for first 
           treeifyBin(tab, hash);
        //  ... 
     }
}


從源碼種可以看出
如果表格是null,resize會初始化它
resize方法兼顧兩個職責,創建初始存儲表格,或者在容量不滿足需求的時候進行擴容
在放置新的鍵值對的時候,如果發生以下情況,就會發生擴容

if (++size > threshold)
    resize();

具體鍵值在哈希表種的位置取決於下面的運算

i = (n - 1) & hash

最後hashMap爲什麼要樹化,本質上是安全問題。因爲在元素放置過程中,如果一個對象的哈希衝突,都被放置在一個桶中,會形成一個鏈表,鏈表查詢是線性的會嚴重影響存取的性能。

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