Java集合-EnumMap源碼實(shí)現(xiàn)分析

概要

EnumMap是專門為枚舉類型量身定做的Map實(shí)現(xiàn)。

示例

先來看個示例代碼:

public class EnumTest {
    
    public enum Color {
        red, blue, black, yellow, green
    }
    
    public static void main(String[] args) {
        EnumMap<Color,String> map = new EnumMap<>(Color.class);
        map.put(Color.yellow, "黃色");
        map.put(Color.blue, "藍(lán)色");
        map.put(Color.red, "紅色");
        map.put(Color.black, "黑色");
        map.put(Color.green, "綠色");

        for(Map.Entry<Color,String> entry : map.entrySet()){
            System.out.println(entry.getKey()+":"+entry.getValue());
        }
        System.out.println(map);
    }
}

輸出:

red:紅色
blue:藍(lán)色
black:黑色
yellow:黃色
green:綠色
{red=紅色, blue=藍(lán)色, black=黑色, yellow=黃色, green=綠色}

從輸出來看躺酒,迭代的順序與Color枚舉類型下定義的值順序一致是越。

EnumMap定義和數(shù)據(jù)結(jié)構(gòu)

類定義如下:

public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
    implements java.io.Serializable, Cloneable {

    // 枚舉類的class對象洒闸,例如示例的Color.class
    private final Class<K> keyType;
    
    // 存儲key的數(shù)組染坯,key即枚舉類的值對象,包括了name和ordinal兩個屬性
    private transient K[] keyUniverse;
    
    // 存儲value的數(shù)組丘逸,value允許null单鹿,null會被轉(zhuǎn)換成Object NULL實(shí)例替代存儲
    private transient Object[] vals;
    
    private transient int size = 0;
    
    // Object的實(shí)例,用于代表null深纲,用于區(qū)分值數(shù)組中元素本身是null(還未存值)仲锄,
    // 還是存儲的就是null值(已經(jīng)存值)
    private static final Object NULL = new Object() {
        public int hashCode() {
            return 0;
        }

        public String toString() {
            return "java.util.EnumMap.NULL";
        }
    };

    private Object maskNull(Object value) {
        return (value == null ? NULL : value);
    }

    @SuppressWarnings("unchecked")
    private V unmaskNull(Object value) {
        return (V)(value == NULL ? null : value);
    }
    
    // 其他省略
}

根據(jù)類定義,存儲結(jié)構(gòu)圖如下:

EnumMap數(shù)據(jù)結(jié)構(gòu)圖

EnumMap采用兩個獨(dú)立的數(shù)組分別維護(hù)key和value湃鹊。由于EnumMap的key必須為指定的枚舉類的類型儒喊,而枚舉類下的值數(shù)量和ordinal(聲明次序)已經(jīng)固定,因此數(shù)組的容量大小在構(gòu)造方法中就已經(jīng)固定了币呵。key存在keyUniverse數(shù)組指定的下標(biāo)中怀愧,value也存在vals相同的下標(biāo)中。這樣key和value就建立了邏輯上的關(guān)系余赢,便于get和put芯义。

如果存一個null值,如下妻柒,則null會做特殊處理扛拨,轉(zhuǎn)成Object NULL實(shí)例后存儲,這樣是為了區(qū)分索引下標(biāo)的元素是否已經(jīng)映射举塔,沒有映射則為null绑警,映射為null了則用NULL實(shí)例進(jìn)行占位替換求泰。

put(Color.blue, null)

基本操作

EnumMap的基本操作都比較快,都在常量時間內(nèi)完成计盒。

1拜秧、put方法

public V put(K key, V value) {
    typeCheck(key);

    int index = key.ordinal();
    Object oldValue = vals[index];
    vals[index] = maskNull(value);
    if (oldValue == null)
        size++;
    return unmaskNull(oldValue);
}

private void typeCheck(K key) {
    Class<?> keyClass = key.getClass();
    if (keyClass != keyType && keyClass.getSuperclass() != keyType)
        throw new ClassCastException(keyClass + " != " + keyType);
}

步驟:
1、檢查key的類型是否是枚舉類的類型章郁,否則拋出ClassCastException異常。
2志衍、用key的ordinal(聲明次序值)作為數(shù)組的索引下標(biāo)暖庄。
3、將value存到數(shù)組key的下標(biāo)里面楼肪。如果是null元素轉(zhuǎn)換為NULL實(shí)例存儲培廓。
4、更新元素數(shù)量計(jì)數(shù)器size春叫。

2肩钠、get方法

public V get(Object key) {
    return (isValidKey(key) ?
            unmaskNull(vals[((Enum<?>)key).ordinal()]) : null);
}
private boolean isValidKey(Object key) {
    if (key == null)
        return false;

    // Cheaper than instanceof Enum followed by getDeclaringClass
    Class<?> keyClass = key.getClass();
    return keyClass == keyType || keyClass.getSuperclass() == keyType;
}

步驟:
1、檢查key的類型是否為相應(yīng)枚舉類的類型暂殖,key為null或者類型不匹配返回null价匠。
2、用key的ordinal值作為數(shù)組的索引下標(biāo)呛每,查找元素并返回踩窖,如果為NULL實(shí)例,則轉(zhuǎn)換為null后返回晨横。


總結(jié)

EnumMap是專門為枚舉類型量身定做的Map實(shí)現(xiàn)洋腮。雖然使用其它的Map實(shí)現(xiàn)(如HashMap)也能完成枚舉類型實(shí)例到值得映射,但是使用EnumMap會更加高效:它只能接收同一枚舉類型的實(shí)例作為鍵值手形,并且由于枚舉類型實(shí)例的數(shù)量相對固定并且有限啥供,所以EnumMap使用數(shù)組來存放與枚舉類型對應(yīng)的值。這使得EnumMap的效率非常高库糠。EnumMap在內(nèi)部使用枚舉類型的ordinal()得到當(dāng)前實(shí)例的聲明次序伙狐,并使用這個次序維護(hù)枚舉類型實(shí)例對應(yīng)值在數(shù)組的位置。

1曼玩、父類為AbstractMap鳞骤,未實(shí)現(xiàn)Map接口,只實(shí)現(xiàn)了Cloneable和Serializable接口黍判。
2豫尽、非線程安全,所有方法和操作都未加鎖顷帖。
3美旧、采用key數(shù)組和vals數(shù)組共同實(shí)現(xiàn)key和value的關(guān)聯(lián)渤滞。
4、不允許null key榴嗅,但允許null value妄呕。
5、null值會被轉(zhuǎn)換為Object的NULL實(shí)例占位替換嗽测。
6绪励、元素的存儲順序按照枚舉值的聲明次序存儲。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末唠粥,一起剝皮案震驚了整個濱河市疏魏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晤愧,老刑警劉巖大莫,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異官份,居然都是意外死亡只厘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進(jìn)店門舅巷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來羔味,“玉大人,你說我怎么就攤上這事钠右〗槠溃” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵爬舰,是天一觀的道長们陆。 經(jīng)常有香客問我,道長情屹,這世上最難降的妖魔是什么坪仇? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮垃你,結(jié)果婚禮上椅文,老公的妹妹穿的比我還像新娘。我一直安慰自己惜颇,他們只是感情好皆刺,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凌摄,像睡著了一般羡蛾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锨亏,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天痴怨,我揣著相機(jī)與錄音忙干,去河邊找鬼。 笑死浪藻,一個胖子當(dāng)著我的面吹牛捐迫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播爱葵,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼施戴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了萌丈?” 一聲冷哼從身側(cè)響起暇韧,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎浓瞪,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體巧婶,經(jīng)...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乾颁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了艺栈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片英岭。...
    茶點(diǎn)故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖湿右,靈堂內(nèi)的尸體忽然破棺而出诅妹,到底是詐尸還是另有隱情,我是刑警寧澤毅人,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布吭狡,位于F島的核電站,受9級特大地震影響丈莺,放射性物質(zhì)發(fā)生泄漏划煮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一缔俄、第九天 我趴在偏房一處隱蔽的房頂上張望弛秋。 院中可真熱鬧,春花似錦俐载、人聲如沸蟹略。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至料仗,卻和暖如春状婶,著一層夾襖步出監(jiān)牢的瞬間茅茂,已是汗流浹背捏萍。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留空闲,地道東北人令杈。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓,卻偏偏與公主長得像碴倾,于是被迫代替她去往敵國和親逗噩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 一、基本數(shù)據(jù)類型 注釋 單行注釋:// 區(qū)域注釋:/* */ 文檔注釋:/** */ 數(shù)值 對于byte類型而言...
    龍貓小爺閱讀 4,257評論 0 16
  • 對象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法僧须,而不是構(gòu)造函數(shù)創(chuàng)建對象:僅僅是創(chuàng)建對象的方法纲刀,并非Fa...
    孫小磊閱讀 1,969評論 0 3
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法担平,內(nèi)部類的語法示绊,繼承相關(guān)的語法,異常的語法暂论,線程的語...
    子非魚_t_閱讀 31,599評論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理面褐,服務(wù)發(fā)現(xiàn),斷路器取胎,智...
    卡卡羅2017閱讀 134,637評論 18 139
  • EnumMap定義 1 以java7進(jìn)行說明 2 成員屬性說明1)EnumMap的鍵是繼承Enum類型的對象2)成...
    paulpaullong閱讀 308評論 0 1