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ì)很亂、臃腫,編寫容易出錯(cuò)。EnumSet可以實(shí)現(xià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í)例,具體過程參見上一篇博文。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ù)不同罷了。這里指只說單元素的情況。getDeclaringClass獲取e的枚舉類型。這里有個(gè)問題,為什么不用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í)取父類
}說回來,獲取類型之后,調(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并沒有覆寫,而是交給兩個(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最終算的就是枚舉值對應(yīng)的比特位是否被置位。
總結(jié)
枚舉自帶自增長的屬性ordinal,可以和位運(yùn)算完美的結(jié)合在一起。
EnumSet的根本就是利用了這個(gè)特性,將ordinal和位運(yùn)算封裝起來,用戶只需要像使用Set一樣使用它。
到此這篇關(guān)于Java枚舉之EnumSet詳解的文章就介紹到這了,更多相關(guān)Java枚舉EnumSet內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java?Swing實(shí)現(xiàn)自定義按鈕組件的完整代碼
本文介紹了Java?Swing按鈕工具類,用于簡化按鈕創(chuàng)建,它封裝了多種按鈕樣式、圖標(biāo)及事件處理,支持懸停效果,開發(fā)者僅需一行代碼即可創(chuàng)建規(guī)范按鈕,大幅提升開發(fā)效率,文中附有完整源碼2026-05-05
Java API如何實(shí)現(xiàn)向Hive批量導(dǎo)入數(shù)據(jù)
這篇文章主要介紹了Java API如何實(shí)現(xiàn)向Hive批量導(dǎo)入數(shù)據(jù)的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
SpringBoot啟動(dòng)時(shí)執(zhí)行特定代碼的10種方式
在實(shí)際的 Spring Boot 項(xiàng)目開發(fā)中,我們經(jīng)常需要在應(yīng)用啟動(dòng)時(shí)執(zhí)行一些特定的初始化代碼,本文將全面深入地介紹 Spring Boot 中啟動(dòng)時(shí)執(zhí)行特定代碼的 10 種方式,需要的朋友可以參考下2025-10-10
java實(shí)現(xiàn)解析Cron時(shí)間表達(dá)式為中文描述
這篇文章主要為大家詳細(xì)介紹了java如何實(shí)現(xiàn)解析Cron時(shí)間表達(dá)式為中文描述,文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下2023-11-11

