Enum、EnumMap、EnumSet的用法講解

在工作中遇到這樣一個問題,要在前臺頁面上渲染出來所有的枚舉元素,但是前臺頁面用的是Velocity,所以需要把枚舉元素放到Map里,然后在前臺進行渲染。這里不能一個一個的取出枚舉元素來put的,就想能用一個循環能搞定這個。發現枚舉類有一個方法values,正好可以滿足我的需求。OK話不多說我們一個一個的介紹其中主要的方法。
先看定義的枚舉類:

package com.zkn.newlearn.enums;

/**
 * 
 * @author zkn 2016-07-11
 *
 */

public enum EnumTest01 {
    
    UPDATE(1,"更新"),QUERY(2,"查詢"),DELETE(3,"刪除");
    private Integer enumValue;
    private String enumDesc;
    
    private EnumTest01(Integer enumValue, String enumDesc) {
        this.enumValue = enumValue;
        this.enumDesc = enumDesc;
    }
    
    public int getEnumValue(){
        return this.enumValue;
    }
    
    public String getEnumDesc(){
        return this.enumDesc;
    }
}

java.lang.Enum

每一個枚舉類都會隱式的繼承java.lang.Enum這個類,也許你會問,你是怎么知道的?我們可以通過反射的方式來解析下EnunTest01這個枚舉類。

    public void testEnumMethod(){
        
        Class clazz = EnumTest01.class;
        Class clazzSuper = clazz.getSuperclass();
        if(clazzSuper != null)
            System.out.println(clazzSuper.getName());
    }

輸出結果為:java.lang.Enum。正是因為這個原因,而java中又是沒有多繼承的,所以枚舉類不能繼承。但是可以實現接口。

public enum EnumTest02 implements Sender{
    ;

    @Override
    public void doSend() {
        // TODO Auto-generated method stub
        
    }
    
}
package com.zkn.newlearn.gof.factory;
/**
 * 
 * @author zkn
 *
 */
public interface Sender {

    public void doSend();
    
}

values方法

通過翻父類中的方法,你也找不到這個方法在哪里。那么這個方法是怎么產生的呢?是編譯器在編譯枚舉類的時候添加上去的。這個我們同樣可以通過反射的方式來獲得這樣的結果。

    public void testEnumMethod(){
        
        Class clazz = EnumTest01.class;
        Method[] methods = clazz.getDeclaredMethods();
        for(Method method : methods){
            System.out.println(method.getName());
        }
        
    }

輸出結果為:
可以看到其中有一個values方法。
那么這個values方法有什么用呢? 通過values方法我們可以獲取枚舉類中的所有的元素。請看例子:

        System.out.println(Arrays.toString(EnumTest01.values()));
        //循環所有的枚舉類型
        for(EnumTest01 test : EnumTest01.values()){
            System.out.println(test.getEnumDesc()+test.getEnumValue());
            //System.out.println(test.name()+"   "+test.ordinal());
        }

輸出結果為:


20160711234616585.png

。當你需要獲取一個枚舉類中的所有枚舉元素的時候,就可以用這個方法了。

valueOf方法

這個方法同樣是編譯在編譯的時候添加到枚舉類中去的。例子見上面。這個方法的作用是通過枚舉元素的名稱獲取一個枚舉類。

System.out.println(EnumTest01.valueOf("UPDATE").getEnumDesc());

與之相對應的是java.lang.Enum這個父類中也有一個valueOf方法。但是這個方法的用法稍微有點不同。如下所示:

System.out.println(Enum.valueOf(EnumTest01.class, "DELETE").getEnumDesc());

switch語句

從java1.6之后,我們可以在switch語句的case中放入枚舉元素。例子如下:

    public void testSwitchEnum() {
        putSwitchEnum(EnumTest01.DELETE);
    }

    public void putSwitchEnum(EnumTest01 enumSwitch) {

        switch (enumSwitch) {
        case DELETE:
            System.out.println("這個是delete方法");
            break;
        case UPDATE:
            System.out.println("這個是update方法");
            break;
        default:
            System.out.println("這個是query方法");
            break;
        }
    }

枚舉元素自定義方法

在枚舉元素中可以自定義方法,來重寫枚舉類中的方法。

package com.zkn.newlearn.enums;
/**
 * 
 * @author zkn 2016-07-11
 *
 */
public enum EnumTest02{
    GREEN{
        @Override
        public void getInfo(){
            System.out.println("這個是綠燈");
        }
        public void getPrint(){
            System.out.println("zhangsan");
        }
    },
    RED{
        @Override
        public void getInfo(){
            System.out.println("這個是紅燈");
        }
    },
    YELLOW{
        @Override
        public void getInfo(){
            System.out.println("這個是黃燈");
        }
    };

    public void getInfo(){
        
    }
    
}

為什么可以這樣做呢?我們看一下編譯器編譯之后的Class文件:如圖:

Paste_Image.png

我們可以這樣去理解,每一個枚舉元素都是一個繼承枚舉類的內部子類,并且每一個內部子類中都包含所有的枚舉元素。下面這個例子可以佐證:

    import static com.zkn.newlearn.enums.EnumTest02.*;
    @Test
    public void testEnumCustom(){
        EnumTest02 enumTest = GREEN;
        enumTest.RED.RED.getInfo();
    } 

實現單例

估計這是最簡單的實現單例的一個方式。只有一個枚舉元素。

package com.zkn.newlearn.gof.singleton;
/**
 * 枚舉實現單例
 * @author zkn
 *
 */
public enum SingletonGofTest04 {

    singleton;
}

ordinal方法

這個方法是用來表示枚舉類中元素的順序的,是從0開始的。這個我們在后面會用到。
for (EnumTest01 test : EnumTest01.values()) {
System.out.println(test.name()+" "+test.ordinal());
}

Paste_Image.png

name方法

name方法用來表示枚舉元素的名稱的,他的名稱即你寫的枚舉元素。例子見上圖。
另外:
你可以像寫java類一樣去寫一個枚舉類,只是這個枚舉類不能集成其他類。

EnumMap

Map的實現類有很多種,EnumMap從名字我們可以看出這個Map是給枚舉類用的。它的key為枚舉元素,value自定義。在工作中我們也可以用其他的Map來實現我們關于枚舉的需求,但是為什么要用這個EnumMap呢?因為它的性能高!為什么性能高?因為它的內部是用數組的數據結構來維護的!我們可以看一下它的源碼實現:

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);
    }

typeCheck是用來檢查key的類型的,因為key只能為枚舉元素。接下來的這一句int index = key.ordinal();key.ordinal()這個就是我們上面說的枚舉類型的序號,然后被當做數組的下標,放到vals這個數組里。那么get方法呢?

get方法

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

注意這一句話:vals[((Enum<?>)key).ordinal()]。這個不就是取得下標,根據下標獲取數組中的值嗎?!

remove方法

    public V remove(Object key) {
        if (!isValidKey(key))
            return null;
        int index = ((Enum<?>)key).ordinal();
        Object oldValue = vals[index];
        vals[index] = null;
        if (oldValue != null)
            size--;
        return unmaskNull(oldValue);
    }

remove方法的實現也是挺簡單的,就是把相應下標的元素變為null,等著GC回收。
這里我們只是說了EnumMap里比較常用的三個方法,如果有興趣的同學可以看看其他的方法實現。一個使用EnumMap的例子奉上:

        EnumMap<EnumTest01, String> enumMap = new EnumMap<EnumTest01, String>(EnumTest01.class);
        enumMap.put(EnumTest01.DELETE, "dsdsd");
        enumMap.put(EnumTest01.UPDATE, "qqqqqq");
        for (Map.Entry<EnumTest01, String> entry : enumMap.entrySet()) {
            System.out.println(entry.getValue() + entry.getKey().getEnumDesc());
        }

EnumSet

EnumSet這是一個用來操作Enum的集合,是一個抽象類,它有兩個繼承類:JumboEnumSet和RegularEnumSet。在使用的時候,需要制定枚舉類型。它的特點也是速度非常快,為什么速度很快呢?因為每次add的時候,每個枚舉值只占一個長整型的一位。我們可以翻看源碼來看看它的實現:

add方法

    public boolean add(E e) {
        typeCheck(e);

        long oldElements = elements;
        elements |= (1L << ((Enum<?>)e).ordinal());
        return elements != oldElements;
    }

從中我們可以看出是先對一個長整型左移枚舉類型的序列數,然后再和長整型 或 。

of方法

of方法有好幾個重載的方法,它的作用是創建一個最初包含指定元素的枚舉 set。

EnumSet<EnumTest01> enumSets = EnumSet.of(EnumTest01.DELETE);

allOf

創建一個包含指定元素類型的所有元素的枚舉 set。

EnumSet<EnumTest01> enumSets = EnumSet.allOf(EnumTest01.class);

range方法

創建一個指定范圍的Set。

EnumSet<EnumTest01> enumSets = EnumSet.range(EnumTest01.DELETE,EnumTest01.UPDATE);

noneOf方法

創建一個指定枚舉類型的空set。

        EnumSet<EnumTest01> enumSet = EnumSet.noneOf(EnumTest01.class);
        enumSet.add(EnumTest01.DELETE);
        enumSet.add(EnumTest01.UPDATE);
        for (Iterator<EnumTest01> it = enumSet.iterator(); it.hasNext();) {
            System.out.println(it.next().getEnumDesc());
        }
        for (EnumTest01 enumTest : enumSet) {
            System.out.println(enumTest.getEnumDesc() + "  ..... ");
        }

copyOf

創建一個set的并copy所傳入的集合中的枚舉元素。

EnumSet<EnumTest01> enumSets = EnumSet.copyOf(enumSet);

其他方法不再多說,不過大家可以思考一下EnumSetIterator中next方法的實現。
好了,關于枚舉的相關介紹就先到這里吧。有問題的可以評論交流。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容

  • 對象的創建與銷毀 Item 1: 使用static工廠方法,而不是構造函數創建對象:僅僅是創建對象的方法,并非Fa...
    孫小磊閱讀 2,015評論 0 3
  • 5繼承 5.1 類、超類和子類 重用部分代碼,并保留所有域。“is-a”關系,用extends表示。 已存在的類被...
    我快要上天啦閱讀 819評論 1 3
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,721評論 18 399
  • 急急急
    toWyatt閱讀 133評論 0 0
  • 2015年韓國熱門化妝品品牌排行榜 1、雪花秀 雪花秀利用適合于這些人們的韓方制成,其根源也應上溯到5千年。雪花秀...
    peo閱讀 344評論 0 2