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