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型的數的某一位進行表示,所以效率是相當高的。