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

概要

EnumMap是專門為枚舉類型量身定做的Map實現(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, "藍色");
        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:藍色
black:黑色
yellow:黃色
green:綠色
{red=紅色, blue=藍色, 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實例替代存儲
    private transient Object[] vals;
    
    private transient int size = 0;
    
    // Object的實例,用于代表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采用兩個獨立的數(shù)組分別維護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實例后存儲,這樣是為了區(qū)分索引下標(biāo)的元素是否已經(jīng)映射,沒有映射則為null,映射為null了則用NULL實例進行占位替換。

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實例存儲。
4、更新元素數(shù)量計數(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實例,則轉(zhuǎn)換為null后返回。


總結(jié)

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容