這裏寫自定義目錄標題
鍵值對Map家族說明
Java爲數據結構中的鍵值對定義了一個接口java.util.Map,此接口主要有四個常用的實現類,分別是HashMap、Hashtable、LinkedHashMap和TreeMap,類繼承關係如下圖所示:
下面針對各個實現類的特點做一些說明:
- HashMap:它根據鍵的hashCode值存儲數據,大多數情況下可以直接定位到它的值,因而具有很快的訪問速度,但遍歷順序卻是不確定的。 HashMap最多隻允許一條記錄的鍵爲null,允許多條記錄的值爲null。HashMap非線程安全,即任一時刻可以有多個線程同時寫HashMap,可能會導致數據的不一致。如果需要滿足線程安全,可以用 Collections的synchronizedMap方法使HashMap具有線程安全的能力,或者使用ConcurrentHashMap。
- Hashtable:Hashtable是遺留類,很多映射的常用功能與HashMap類似,不同的是它承自Dictionary類,並且是線程安全的,任一時間只有一個線程能寫Hashtable,併發性不如ConcurrentHashMap,因爲ConcurrentHashMap引入了分段鎖。Hashtable不建議在新代碼中使用,不需要線程安全的場合可以用HashMap替換,需要線程安全的場合可以用ConcurrentHashMap替換。
- LinkedHashMap:LinkedHashMap是HashMap的一個子類,保存了記錄的插入順序,在用Iterator遍歷LinkedHashMap時,先得到的記錄肯定是先插入的,也可以在構造時帶參數,按照訪問次序排序。
- TreeMap:TreeMap實現SortedMap接口,能夠把它保存的記錄根據鍵排序,默認是按鍵值的升序排序,也可以指定排序的比較器,當用Iterator遍歷TreeMap時,得到的記錄是排過序的。如果使用排序的映射,建議使用TreeMap。在使用TreeMap時,key必須實現Comparable接口或者在構造TreeMap傳入自定義的Comparator,否則會在運行時拋出java.lang.ClassCastException類型的異常。
對於上述四種Map類型的類,要求映射中的key是不可變對象。不可變對象是該對象在創建後它的哈希值不會被改變。如果對象的哈希值發生變化,Map對象很可能就定位不到映射的位置了。
HashMap存儲結構詳解
首先來看JDK8中HashMap存儲結構示例代碼
/** Jdk8 HashMap 分解詳解 */
public class HashMapAnalysis<K,V> {
/** Node實現了Map.Entry<K,V>用於數據存儲鍵值對,如果K的hashCode相同,存儲在Node實例中鏈表結構next當中 */
transient Node<K,V>[] table;
/** 當前Map的鍵值對的數量 */
transient int size;
/** Map需要擴容的負載因子,默認0.75 */
final float loadFactor = 0.75f ;
/** 下一個元素大小,如果達到擴容數字(容量*加載因子),需要resize(雙倍擴容) */
int threshold;
/** HashMap中存儲對象Node對象結構 */
private class Node<K, V> implements Map.Entry<K,V>{
/** 當前Key的hashCode值 */
final int hash;
/** 當前Entry中Key對象 */
final K key;
/** 當前Entry中Value對象 */
V value;
/** 當前Entry中Key的hashCode一樣Entry的下一個Entry(鏈表存儲) */
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;
}
}
}
看屬性元素的說明,就能知道意思了,也可以使用下圖來理解:
HashMap非線程安全導致死循環問題說明
在多線程工作環境下,HashMap容量達到臨界值的put操作後,其他線程get操作容易導致死循環。是因爲HashMap容量達到臨界值會進行2倍擴容,擴容完成之後會進行數據轉移的工作在transfer(JDK1.8中已沒有此方法)方法中,單線程時相同hashCode的Entry鏈表數據轉移完成後元素會倒序,如果此時發生多線程操作,就容易造成死循環,死循環原理詳解本文不詳細說明(內容較多可另起文章說明)。ConcurrentHashMap將原本HashMap的單個數組存儲形式,分解成多個段(Segment)的形態,每個段裏面的結構和以前HashMap結構類似,這樣使用分段鎖,來減少鎖的範圍,提高併發效率。
HashMap常見面試題
HashTable, HashMap,TreeMap區別?
- HashTable線程同步,HashMap非線程同步
- HashTable不允許<鍵,值>有空值,HashMap允許<鍵,值>有空值
- HashTable使用Enumeration,HashMap使用Iterator
- HashTable中hash數組的默認大小是11,增加方式的old*2+1,HashMap中hash數組的默認大小是16,增長方式一定是2的指數倍。
- TreeMap能夠把它保存的記錄根據鍵排序,默認是按升序排序
HashMap是不是有序的連環炮式發問
【你肯定回答說,不是有序的】。那面試官就會繼續問你,有沒有有順序的Map實現類? 【你說有TreeMap和LinkedHashMap】。 那麼面試官接下來就可能會問你,TreeMap和LinkedHashMap是如何保證它的順序的?【LinkedHashMap根據put的順序保證有序,TreeMap根據Key元素的Comparable接口實現方法保證有序】。 如果你依然回答上來了,那麼面試官還會繼續問你,你覺得它們兩個哪個的有序實現比較好? 如果你依然可以回答的話,那麼面試官會繼續問你,你覺得還有沒有比它更好或者更高效的實現方式? 如果你還能說出來的話,那麼就你所說的實現方式肯定依然可以問你很多問題。 以上就是一個面試官一步一步提問的例子。所以,如果你瞭解的不多,千萬不要敷衍,因爲可能下一個問題你就暴露了,還不如直接說不會,把這個問題結束掉,趕緊切換到你熟悉的領域。