Java枚舉之EnumSet詳解
EnumSet
當(dāng)需要使用位運(yùn)算時(shí),可能你會(huì)如此定義
private final static int FIRST = 1; private final static int SECOND = 1 << 1; private final static int THIRD = 1 << 2;
使用時(shí)進(jìn)行與或運(yùn)算。但是定義多了之后,會(huì)很亂、臃腫,編寫(xiě)容易出錯(cuò)。EnumSet可以實(shí)現(xiàn)類似的功能,且使用起來(lái)很簡(jiǎn)潔。
示例
public enum EnumTest {
FIRST,
SECOND;
public static void main(String[] args) {
EnumSet<EnumTest> enumTests = EnumSet.allOf(EnumTest.class);
//輸出enumTests所有元素
System.out.println(enumTests);//[FIRST, SECOND]
//判斷集中中是否存在
System.out.println(enumTests.contains(EnumTest.SECOND));//true
}
}屬性
final Class<E> elementType; final Enum<?>[] universe;
elementType為EnumSet存儲(chǔ)的元素類型,示例中為EnumTest。universe為存儲(chǔ)的所有枚舉實(shí)例數(shù)組。
noneOf
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum<?>[] universe = getUniverse(elementType);//獲取枚舉類型的所有實(shí)例
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
if (universe.length <= 64)//判斷枚舉實(shí)例的數(shù)目
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
}首先獲取枚舉的所有實(shí)例,具體過(guò)程參見(jiàn)上一篇博文。RegularEnumSet和JumboEnumSet為EnumSet的兩個(gè)實(shí)現(xiàn)類。
當(dāng)枚舉中定義的實(shí)例數(shù)不大于64時(shí),生成RegularEnumSet,否則生成JumboEnumSet。
allOf
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) {
EnumSet<E> result = noneOf(elementType);
result.addAll();
return result;
}在allOf先調(diào)用noneOf獲得EnumSet(RegularEnumSet或JumboEnumSet),allAll是個(gè)抽象方法,看下RegularEnumSet中是如何實(shí)現(xiàn)的
//RegularEnumSet.java
private long elements = 0L;
void addAll() {
if (universe.length != 0)
elements = -1L >>> -universe.length;
}elements代表了EnumSet中存儲(chǔ)的所有枚舉實(shí)例所代表的bit數(shù)。示例中存儲(chǔ)了FIRST和SECOND,分別代表了1和2,所以allAll之后,elements為3。
of
public static <E extends Enum<E>> EnumSet<E> of(E e) {
EnumSet<E> result = noneOf(e.getDeclaringClass());
result.add(e);
return result;
}of方法有好幾個(gè),參數(shù)不同罷了。這里指只說(shuō)單元素的情況。getDeclaringClass獲取e的枚舉類型。這里有個(gè)問(wèn)題,為什么不用getClass。在示例中,這兩種返回的都是EnumTest。再看另一種情況
public enum EnumTest {
FIRST {
@Override
void doSometing() {
}
},
SECOND {
@Override
void doSometing() {
}
};
abstract void doSometing();
public static void main(String[] args) {
System.out.println(EnumTest.SECOND.getClass());//EnumTest$2
System.out.println(EnumTest.SECOND.getDeclaringClass());//EnumTest
}
}當(dāng)枚舉有方法實(shí)現(xiàn)時(shí),此時(shí)相當(dāng)于內(nèi)部類,getClass和getDeclaringClass返回的結(jié)果就不一樣了。
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();//獲取自己的Class
Class<?> zuper = clazz.getSuperclass();//獲取父類的Class
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;//兩者不等時(shí)取父類
}說(shuō)回來(lái),獲取類型之后,調(diào)用noneOf。調(diào)用add方法。
//RegularEnumSet.java
public boolean add(E e) {
typeCheck(e);//校驗(yàn)e的類型是否和生成elementType一致
long oldElements = elements;
elements |= (1L << ((Enum<?>)e).ordinal());
return elements != oldElements;
}
如果此時(shí)e為EnumTest.SECOND,ordinal為1,那么elements與運(yùn)算后則為2。
contains
contains為AbstractCollection中的方法,EnumSet并沒(méi)有覆寫(xiě),而是交給兩個(gè)子類去實(shí)現(xiàn)的。
//RegularEnumSet.java
public boolean contains(Object e) {
if (e == null)//判空
return false;
Class<?> eClass = e.getClass();//校驗(yàn)類型
if (eClass != elementType && eClass.getSuperclass() != elementType)
return false;
//比特位運(yùn)算
return (elements & (1L << ((Enum<?>)e).ordinal())) != 0;
}contains最終算的就是枚舉值對(duì)應(yīng)的比特位是否被置位。
總結(jié)
枚舉自帶自增長(zhǎng)的屬性ordinal,可以和位運(yùn)算完美的結(jié)合在一起。
EnumSet的根本就是利用了這個(gè)特性,將ordinal和位運(yùn)算封裝起來(lái),用戶只需要像使用Set一樣使用它。
到此這篇關(guān)于Java枚舉之EnumSet詳解的文章就介紹到這了,更多相關(guān)Java枚舉EnumSet內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java線程中斷?interrupt?和?LockSupport解析
這篇文章主要為大家介紹了java線程中斷?interrupt?和?LockSupport示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
springcloud?feign?接口指定接口服務(wù)ip方式
這篇文章主要介紹了springcloud?feign?接口指定接口服務(wù)ip方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
javaweb啟動(dòng)時(shí)啟動(dòng)socket服務(wù)端代碼實(shí)現(xiàn)
這篇文章主要介紹了javaweb啟動(dòng)時(shí)啟動(dòng)socket服務(wù)端代碼實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
Springboot實(shí)現(xiàn)高吞吐量異步處理詳解(適用于高并發(fā)場(chǎng)景)
這篇文章主要介紹了Springboot實(shí)現(xiàn)高吞吐量異步處理詳解(適用于高并發(fā)場(chǎng)景),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
SpringBoot實(shí)現(xiàn)導(dǎo)出復(fù)雜對(duì)象到Excel文件
這篇文章主要為大家詳細(xì)介紹了如何使用Hutool和EasyExcel兩種方式來(lái)實(shí)現(xiàn)在Spring Boot項(xiàng)目中導(dǎo)出復(fù)雜對(duì)象到Excel文件,需要的小伙伴可以參考下2025-03-03
springboot集成websocket的四種方式小結(jié)
本文主要介紹了springboot集成websocket的四種方式小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12

