Java EnumSet工作原理初窺


  EnumSet是Java枚舉類型的泛型容器,Java既然有了SortedSet、TreeSet、HashSet等容器,為何還要多一個EnumSet<T>呢?答案肯定是EnumSet有一定的特性,舉個例子,EnumSet的速度很快。其他特性就不一一列舉了,畢竟本文的內容不是介紹EnumSet的特性。

  首先以事實說話,存在這樣一個EnumSet,它有50個枚舉值T0~T49,將50個值插入到容器(HashSet、EnumSet)中,為一個操作,將50個枚舉值移出做為第二個操作。把第一個和第二個操作執行的總時間設定為一個周期,拿HashSet操作的一個周期和EnumSet的一個周期做比較自然沒什么意義,所以我們用50個周期的和做為比較,HashSet耗費9ms,EnumSet耗費4ms(這個結果只說明同樣的操作EnumSet比HashSet更快,不做為其他參考依據,因為這個時間不是線程獨占時間)。以下是代碼和結果:

 

public class EnumSetTest{
    
    private static EnumTest[] enumTestArr = EnumTest.values();

    public static void main(String[] args) {
        Set set = new HashSet<EnumTest>();
        int i = 0;
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        System.out.println("HashSet...Begin " + df.format(new Date()));
        while (i <= 1000) {
            addEnumerate(set);
            removeEnumerate(set);
            i++;
        }
        System.out.println("HashSet...End  " + df.format(new Date()));

        EnumSet<EnumTest> enumSet = EnumSet.noneOf(EnumTest.class);
        i = 0;
        System.out.println("EnumSet...Begin " + df.format(new Date()));
        while (i <= 1000) {
            addEnumerate(enumSet);
            removeEnumerate(enumSet);
            i++;
        }
        System.out.println("EnumSet...End " + df.format(new Date()));
    }
    /*
    * HashSet...Begin 2015-01-03 21:11:51.579
    * HashSet...End  2015-01-03 21:11:51.588
    * EnumSet...Begin 2015-01-03 21:11:51.589
    * EnumSet...End 2015-01-03 21:11:51.593
    * */

    private static void addEnumerate(Set set) {
        for (EnumTest t : enumTestArr) {
            set.add(t);
        }
    }

    private static void removeEnumerate(Set set) {
        for (EnumTest t : enumTestArr) {
            set.remove(t);
        }
    }
}

那為什么EnumSet比較快呢,EnumSet是一個抽象方法,本次測試是用到的EnumSet的實現RegularEnumSet,RegularEnumSet add方法的源碼如下:

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

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

RegularEnumSet的源碼

從Add方法的源碼可以看出add方法實際只是對長整型數據element做了一個操作,也就是說EnumSet實際上將枚舉值保存在一個長整型數據上。沒個枚舉值占用一bit。每次添加做的主要操作是1、類型檢查 2、 添加枚舉值 3、判斷枚舉值是否已經添加過了。 

現在模擬一個場景來說明EnumSet工作原理。新建一個EnumSet(Set1),並向Set1中添加EnumTest.T3(ordinal:3),代碼如下

        EnumSet<EnumTest> set1 = EnumSet.noneOf(EnumTest.class);
        set1.add(EnumTest.T3);

element本來有64個bit,這里就簡略畫了。這里有一個問題,就是枚舉值的個數超過64個怎么辦?超過64個了就用EnumSet的另一個實現JumboEnumSet。

本文到此就結束了,至於JumboEnumSet的工作原理和RegularEnumSet其他方法的工作原理請自行探索吧。所有源碼都可以在http://grepcode.com/ 找到。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM