EnumSet源碼解析

EnumSet表示一個枚舉類的集合,而且這個集合中只包含同一種枚舉類的對象,下面來看一下這個類型的使用方法:

首先定義一個枚舉類:

public enum Color {
    RED("紅色",0),BLUE("藍色",2),ORANGE("橙色",3);
    private String color;
    private int value;

    Color(String color, int value) {
        this.color = color;
        this.value = value;
    }

    public String getColor() {
        return color;
    }

    public int getValue() {
        return value;
    }
}
下面是一個測試類:

public class EnumSetTest {
  public static void main(String[] args) {
      
      Set<Color> set=
      EnumSet.allOf(Color.class);
      for (Color c:set){
      System.out.println(c.getColor()+" "+c.getValue());
      }
      System.out.println(set.contains(Color.RED));
  }
}

EnumSet是一個抽象類,它有兩個子類:RegularEnumSet、JumboEnumSet,當枚舉類中的元素少於64時,返回RegularEnumSet對象,當元素多於64時,返回JumboEnumSet對象。

具體的代碼如下,通過noneOf()方法實現,當EnumSet需要進行一個操作時首先調用這個方法,獲得一個RegularEnumSet或者JumboEnumSet對象,隨後在執行一些操作。

public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        Enum<?>[] universe = getUniverse(elementType);
        if (universe == null)
            throw new ClassCastException(elementType + " not an enum");

        if (universe.length <= 64)
            return new RegularEnumSet<>(elementType, universe);
        else
            return new JumboEnumSet<>(elementType, universe);
    }
那麼,我們來看一下RegularEnumSet內部的組成結構:

1.add()方法:

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

        long oldElements = elements;
        elements |= (1L << ((Enum<?>)e).ordinal());
        return elements != oldElements;
    }
ordinal()方法返回的是枚舉值,默認從0開始,考慮到long型在java中佔用64位,表示成二進制數就是一個非常長的一串數字,添加第一個元素的時候,elements=00......0001,存入另外的一個元素後,00.....0001|00....0010=00.....0011,直到所有的位都被填充爲1,(嗯?如果超過64怎麼辦?怎麼可能呢?)

2.contain()方法:

 public boolean contains(Object e) {
        if (e == null)
            return false;
        Class<?> eClass = e.getClass();
        if (eClass != elementType && eClass.getSuperclass() != elementType)
            return false;

        return (elements & (1L << ((Enum<?>)e).ordinal())) != 0;
    }
再來看看contain()方法,首先是一個類型校驗,十分的嚴謹,隨後又是一串位運算,比方說從剛剛的添加函數中我們可以看到,添加幾個元素,那麼elements的二進制表示就有多少個1,那麼如果集合中存在這個元素,對應的位就會被置爲1,然後與elements進行與運算,結果就十分明瞭了。

3.remove()方法

public boolean remove(Object e) {
        if (e == null)
            return false;
        Class<?> eClass = e.getClass();
        if (eClass != elementType && eClass.getSuperclass() != elementType)
            return false;

        long oldElements = elements;
        elements &= ~(1L << ((Enum<?>)e).ordinal());
        return elements != oldElements;
    }
remove()方法同理,這是個將對應的位置零的過程,還是舉剛剛的例子:elements爲00..0011,這時候要刪除第二個元素,這是等號右邊爲111...1101,那麼第二位正好會被置爲0,正好將這個元素刪除掉。

這裏選取這三個函數進行一些簡單的講解,再來枚舉類中元素超過64個的情形(好像重來沒有見過...)

首先看一下它的構造函數:

 JumboEnumSet(Class<E>elementType, Enum<?>[] universe) {
        super(elementType, universe);
        elements = new long[(universe.length + 63) >>> 6];//long型數組
    }
嗯?>>>6?!什麼鬼,原來這裏新建了一個數組,假設這裏有65個元素,那麼(65+63)>>>6表示128/64=2,這有什麼好處呢?下面來看一下add()函數:

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

        int eOrdinal = e.ordinal();
        int eWordNum = eOrdinal >>> 6;//表示高位

        long oldElements = elements[eWordNum];
        elements[eWordNum] |= (1L << eOrdinal);//那麼只操作高位了,低位不用再管,爲啥?超過64位了,低位不在考慮
        boolean result = (elements[eWordNum] != oldElements);//看看是否有不同,如果不同表示有新的元素添加
        if (result)
            size++;
        return result;
    }
至此,對於EnumSet的分析告一段落,當然還有一些函數沒有去解析,限於篇幅,這裏選擇一些典型的函數進行講解,最後,通過源碼可以看出枚舉類中的每一個元素在這裏邊只是通過一個long型的數的某一位進行表示,所以效率是相當高的。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章