java中的集合Map

Map的基本實現(xiàn)跨琳,包括:HashMap自点、TreeMap、LinkedHashMap脉让、WeekHashMap桂敛、ConcurrentHashMap、IdentityHashMap溅潜。它們都有同樣的基本接口Map术唬,但是行為特性各不相同,這表現(xiàn)在效率滚澜,鍵值對的保存及呈現(xiàn)次序粗仓、對象的保存周期、映射表如何在多線程程序中工作和判定“鍵”等價的策略等方面设捐。

java集合

java集合圖.gif

HashMap
Map基于散列表的實現(xiàn)(它取代了Hashtable)借浊。插入和查詢“鍵值對”的開銷是固定的÷苷校可以通過構造器設置容量和負載因子巴碗,以調整容器的性能。

LinkedHashMap
類似于HashMap即寒,但是迭代遍歷它時,取得“鍵值對”的順序是其插入次序召噩,或者是最近最少使用(LRU)的次序母赵。只比HashMap慢一點;而在迭代訪問時反而更快具滴,因為它使用鏈表維護內部次序凹嘲。

TreeMap
基于紅黑樹的實現(xiàn)。查看“鍵”或“鍵值對”時构韵,它們會被排序(次序由Comparable或Comparator決定)周蹭。TreeMap的特點在于趋艘,所得到的結果是經過排序的,TreeMap是唯一帶有subMap方法的Map凶朗,它可以返回一個子樹瓷胧。

WeekHashMap
弱鍵(week key)映射,允許釋放映射所指向的對象棚愤;這是為解決某些類特殊問題而設計的搓萧。如果映射之外沒有引用指向某個“鍵”,則此“鍵”可以被垃圾收集器回收宛畦。

ConcurrentHashMap
一種線程安全的Map瘸洛,它不涉及同步加鎖。

IdentityHashMap
使用==代替equals對“鍵”進行比較的散列映射升略,專為解決特殊問題而設計黍图。

Map接口中主要的方法

containsKey(Object key):如果此映射包含指定鍵的映射關系摘仅,則返回 true;
containsValue(Object value):如果此映射將一個或多個鍵映射到指定值石蔗,則返回 true;
entrySet():返回此映射中包含的映射關系的 Set 視圖读规;
get(Object key):返回指定鍵所映射的值抓督;如果此映射不包含該鍵的映射關系,則返回 null束亏;
keySet():返回此映射中包含的鍵的 Set 視圖铃在;
put(K key, V value):將指定的值與此映射中的指定鍵關聯(lián)(可選操作)。

AbstractMap提供 Map 接口的骨干實現(xiàn)碍遍,以最大限度地減少實現(xiàn)Map接口所需的工作定铜。要實現(xiàn)不可修改的映射,編程人員只需擴展此類并提供 entrySet 方法的實現(xiàn)即可怕敬,該方法將返回映射的映射關系Set視圖揣炕。通常,返回的 set 將依次在AbstractSet上實現(xiàn)东跪。此 set 不支持add或remove方法畸陡,其迭代器也不支持 remove 方法。要實現(xiàn)可修改的映射虽填,編程人員必須另外重寫此類的 put 方法(否則將拋出 UnsupportedOperationException)丁恭,entrySet().iterator() 返回的迭代器也必須另外實現(xiàn)其remove方法。

LinkedHashMap返回的結果是其插入次序

LinkedHashMap繼承自HashMap斋日,所以它比HashMap的性能略差牲览,但是可以維護元素間的插入順序(使用一個雙向鏈表來保存順序):

private transient Entry<K,V> header;  
private static class Entry<K,V> extends HashMap.Entry<K,V> {  
        // These fields comprise the doubly linked list used for iteration.  
        Entry<K,V> before, after;  
        …….//省略  
}  

當要調用put方法插入元素時,會調用HashMap的put方法恶守,這個方法會調用addEntry()方法第献,這個方法在LinkedHashMap中被重定義了:

//LinkedHashMap的addEntry方法  
void addEntry(int hash, K key, V value, int bucketIndex) {  
        super.addEntry(hash, key, value, bucketIndex);//調用HashMap中的addEntry方法贡必,會創(chuàng)建結點,同時會維護新創(chuàng)建的結點到雙向鏈表中  
        // Remove eldest entry if instructed  
        Entry<K,V> eldest = header.after;  
        if (removeEldestEntry(eldest)) {  
            removeEntryForKey(eldest.key);  
        }  
}  
//HashMap中的addEntry方法  
void addEntry(int hash, K key, V value, int bucketIndex) {  
        if ((size >= threshold) && (null != table[bucketIndex])) {  
            resize(2 * table.length);  
            hash = (null != key) ? hash(key) : 0;  
            bucketIndex = indexFor(hash, table.length);  
        }  
  
        createEntry(hash, key, value, bucketIndex);  
}  
//LinkedHashMap中的createEntry庸毫,覆蓋HashMap中的createEntry  
void createEntry(int hash, K key, V value, int bucketIndex) {  
        HashMap.Entry<K,V> old = table[bucketIndex];  
        Entry<K,V> e = new Entry<>(hash, key, value, old);  
        table[bucketIndex] = e;  
        e.addBefore(header);  
        size++;  
}  

從以上代碼中我們可以看到LinkedHashMap的put方法的過程仔拟,首先LinkedHashMap中沒有put方法,所以會調用HashMap中的put方法岔绸,這個put方法會檢查數(shù)據是否在Map中理逊,如果不在就會調用addEntry方法,由于LinkedHashMap覆蓋了父類的addEntry方法盒揉,所以會直接調用LinkedHashMap的addEntry方法晋被,這個方法中又調用了HashMap的addEntry方法,addEntry又調用了createEntry方法刚盈,這個方法也是LinkedHashMap覆蓋了HashMap的羡洛,它會創(chuàng)建結點到table中,同時會維護Entry(繼承自HashMap.Entry的LinkedHashMap.Entry)的前后元素藕漱。

//HashMap中的createEntry方法欲侮,對比以上LinkedHashMap中的createEntry方法發(fā)現(xiàn),除了將Entry放入桶中之外肋联,LinkedHashMap還維護了Entry指向之前元素和之后元素的指針  
void createEntry(int hash, K key, V value, int bucketIndex) {  
        Entry<K,V> e = table[bucketIndex];  
        table[bucketIndex] = new Entry<>(hash, key, value, e);  
        size++;  
}  

簡單來講威蕉,LinkedHashMap中的Entry是帶有指向在它自己插入Map之前和之后的元素引用的對象,在put元素時橄仍,首先檢查數(shù)據是否已經在Map中韧涨,如果不在就創(chuàng)建這個Entry,同時還要把這個Entry記錄插入到之前元素構成的鏈表中(并沒有真的簡單的創(chuàng)建了個鏈表結點侮繁,而是這個鏈表本身就是這些Entry元素構成的)虑粥。這些Entry本身不但是Map中table的元素,還是鏈表元素宪哩。
在進行遍歷時娩贷,它使用的是KeyIterator,而KeyIterator繼承自LinkedHashIterator锁孟,在LinkedHashIterator內部有鏈表的頭指針指向的下一個元素:

Entry<K,V> nextEntry = header.after;

由于這些Entry本身是鏈表元素彬祖,也是table中元素,故直接找到其后繼就可以得到所有元素品抽。剩下的遍歷過程就是對一個鏈表的遍歷了涧至,每遍歷到一個Entry就可以獲得它的key和value。

此外桑包,LinkedHashMap還能維護一個最近最少訪問的序列,其本質還是維護Entry指針纺非,每次使用get訪問元素時哑了,都會將這個元素插入Map尾部赘方,這樣鏈表頭部就是最近訪問次數(shù)最少的元素了,整個鏈表就是從近期訪問最少到近期訪問最多的順序弱左。

其實現(xiàn)方式是窄陡,在get中找到要get的元素后調用元素的recordAccess方法,這個方法就把這個Entry的前后指針進行了調整拆火。

//LinkedHashMap的get方法  
public V get(Object key) {  
        Entry<K,V> e = (Entry<K,V>)getEntry(key);  
        if (e == null)  
            return null;  
        e.recordAccess(this);//調整指針  
        return e.value;  
}  
//Entry的recordAccess方法跳夭,參數(shù)m就是一個LinkedHashMap  
void recordAccess(HashMap<K,V> m) {  
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;  
        if (lm.accessOrder) {//是否按照最近最少訪問排列  
            lm.modCount++;  
            remove();//從當前鏈中刪除自己  
            addBefore(lm.header);//加入到鏈表尾部  
        }  
}  

總的來說,對于所有的集合類來說们镜,對于List币叹,如果隨機存取多于修改首尾元素的可能,則應該選擇ArrayList模狭,如果要實現(xiàn)類似隊列或者棧的功能或者首尾添加的功能較多颈抚,則應該選擇LinkedList;對于Set嚼鹉,HashSet是常用的Set贩汉,畢竟通常對Set操作都是插入和查詢,但是如果希望產生帶有排序的Set則可以使用TreeSet锚赤,希望記錄插入順序則要使用LinkedHashSet匹舞;而Map和Set類似,如果需要快速的查詢和添加线脚,則可以用HashMap赐稽,如果需要Map中的元素按照一定的規(guī)則排序,則可以用TreeMap酒贬,如果需要記錄數(shù)據加入Map的順序又憨,或者需要使用最近最少使用的規(guī)則,則可以用LinkedHashMap锭吨。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末蠢莺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子零如,更是在濱河造成了極大的恐慌躏将,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件考蕾,死亡現(xiàn)場離奇詭異祸憋,居然都是意外死亡,警方通過查閱死者的電腦和手機肖卧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門蚯窥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事拦赠∥∩常” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵荷鼠,是天一觀的道長句携。 經常有香客問我,道長允乐,這世上最難降的妖魔是什么矮嫉? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮牍疏,結果婚禮上蠢笋,老公的妹妹穿的比我還像新娘。我一直安慰自己麸澜,他們只是感情好挺尿,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著炊邦,像睡著了一般编矾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上馁害,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天窄俏,我揣著相機與錄音,去河邊找鬼碘菜。 笑死凹蜈,一個胖子當著我的面吹牛,可吹牛的內容都是我干的忍啸。 我是一名探鬼主播仰坦,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼计雌!你這毒婦竟也來了悄晃?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤凿滤,失蹤者是張志新(化名)和其女友劉穎妈橄,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體翁脆,經...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡眷蚓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了反番。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沙热。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡叉钥,死狀恐怖,靈堂內的尸體忽然破棺而出篙贸,到底是詐尸還是另有隱情沼侣,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布歉秫,位于F島的核電站,受9級特大地震影響养铸,放射性物質發(fā)生泄漏雁芙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一钞螟、第九天 我趴在偏房一處隱蔽的房頂上張望兔甘。 院中可真熱鬧,春花似錦鳞滨、人聲如沸洞焙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽澡匪。三九已至,卻和暖如春褒链,著一層夾襖步出監(jiān)牢的瞬間唁情,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工甫匹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留甸鸟,地道東北人。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓兵迅,卻偏偏與公主長得像抢韭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子恍箭,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內容