天天看點

java之Map源代碼淺析

Map是鍵值對。也是經常使用的資料結構。

Map接口定義了map的基本行為。包含最核心的get和put操作,此接口的定義的方法見下圖:

java之Map源代碼淺析

JDK中有不同的的map實作,分别适用于不同的應用場景。如線程安全的hashTable和非線程安全的hashMap.

例如以下圖是JDK中map接口的子類UML類圖,當中有個特例Dictionary已經不建議使用:

java之Map源代碼淺析

Map接口中的方法我們須要關注的就是get、put 和疊代器相關的方法如entrySet()、keySet()、values()方法。

Entry

在開始分析map之前,首先了解map中元素的存儲。我們知道map能夠覺得是鍵值對的集合,java中map使用Entry存儲鍵值對,這是一個接口,其定義例如以下,簡單明了。接口方法主要是對鍵和值進行操作。

AbstractMap

Map接口的抽象實作。見下面示範樣例實作代碼:

*

*通常子類用更有效率的方法覆寫之。

如hashMap中覆寫了keySet 、values 、get方法等

*/

new AbstractMap<String,String>(){

/*

* 傳回map中的元素集合,傳回的集合通常繼承AbstractSet 就可以。

@Override

public Set<Map.Entry<String, String>> entrySet() {

return new AbstractSet<Map.Entry<String,String>>() {

public Iterator<java.util.Map.Entry<String, String>> iterator() {

return null;

}

public int size() {

return 0;

};

* 預設實作抛出異常,可變map須要實作此方法

public String put(String key, String value) {

HashMap

hashMap繼承abstractMap,是相當經常使用的資料結構,採用hash散列的思想。能夠在O(1)的時間複雜度内插入和擷取資料。其基本實作能夠分析上個小節中的抽象方法,文章

淺析HashMap的實作和性能分析 已經對hashMap的實作、put和get操作進行了較具體的說明。

這裡不再贅述。關鍵看他的疊代器實作,這裡僅僅分析下entrySet()方法,而keySet()和values()方法實作與之中的一個脈相承。

關于疊代器。見以下摘出的部分源代碼和相關凝視:

實作和hashMap基本一緻,僅僅是在方法上加上了同步操作。

多線程環境能夠使用它。隻是如今有ConcurrentHashMap了,在高并發的時候,能夠用它替換hashtable.

LinkedHashMap

hashMap可能在某些場景下不符合要求,由于放入到當中的元素是無序的。而LinkedHashMap則在一定程度上解決問題。

其在實作上繼承了HashMap,在存儲上擴充haspMap.enteySet,增加了before、after字段。把hashMap的元素用雙向連結清單連接配接了起來。這個雙向連結清單決定了它的周遊順序。

其順序一般是插入map中的順序,可是它有一個字段accessOrder當為true時,周遊順序将是LRU的效果。

研究它的有序性。我們能夠從put方法、get方法和周遊的方法入手。首先看get方法:

關鍵在于

* e.recordAccess(this) 這句代碼

public V get(Object key) {

Entry<K,V> e = (Entry<K,V>)getEntry(key);

if (e == null)

e.recordAccess(this);

return e.value;

/**

* Entry.recordAccess 方法

* 假設是訪問順序(accessOrder=true),那麼就把它放到頭結點的下一個位置

* 否則什麼也不做。

* 這樣就能夠依據初始 accessOrder 屬性,來決定周遊的順序。

void recordAccess(HashMap<K,V> m) {

LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;

if (lm.accessOrder) {

lm.modCount++;

remove();

addBefore(lm.header);

Put方法:

private void addBefore(Entry<K,V> existingEntry) {

after = existingEntry;

before = existingEntry.before;

before.after = this;

after.before = this;

周遊疊代直接使用雙向連結清單進行疊代接口,這裡不贅述。能夠看源代碼非常easy了解。

注意的是實作上市覆寫了父類中相關的生成疊代器的方法。

TreeMap和CurrentHashMap都能夠單獨開一篇文章來分析了。這裡簡單說下。TreeMap是基于b樹map,依據key排序。CurrentHashMap是并發包中的一個強大的類,适合多線程高并發時資料讀寫。