Java8 EnumSet 源码简单分析
一、EnumSet 概述
EnumSet 是一個用于存儲枚舉類型的 set 集合,一般的 set 集合底層都是使用對應(yīng)的 map 實現(xiàn)的,但 EnumSet 是個特例,它底層使用一個 long 類型的整數(shù)或者數(shù)組實現(xiàn)。
下面我們先來看下它的使用方式:
public class EnumSetTest {public static void main(String[] args) {EnumSet<Season> e1 = EnumSet.allOf(Season.class);System.out.println(e1);EnumSet<Season> e2 = EnumSet.noneOf(Season.class);e2.add(Season.SPRING);System.out.println(e2);EnumSet<Season> e3 = EnumSet.of(Season.SPRING, Season.FAIL);System.out.println(e3);EnumSet<Season> e4 = EnumSet.range(Season.SPRING, Season.FAIL);System.out.println(e4);} }enum Season {SPRING,SUMMER,FAIL,WINTER }輸出結(jié)果:
[SPRING, SUMMER, FAIL, WINTER]
[SPRING]
[SPRING, FAIL]
[SPRING, SUMMER, FAIL]
[WINTER]
一般我們不使用構(gòu)造函數(shù)來直接定義 EnumSet,EnumSet 提供了很靈活的方法可以供使用者有選擇的進(jìn)行初始化。EnumSet 本身是一個抽象類,對于增刪改查的方法都放在了子類中去實現(xiàn),這兩個子類分別是:RegularEnumSet 與 JumboEnumSet。他們之間的主要區(qū)別是如果枚舉類型中的元素大于 64 時,則創(chuàng)建 JumboEnumSet 對象,內(nèi)部使用 long 類型的數(shù)組實現(xiàn),反之創(chuàng)建 RegularEnumSet,內(nèi)部使用 long 類型整數(shù)實現(xiàn)。
二、源碼分析
2.1 初始化
我們以 noneOf 方法為例,來看下 EnumSet 是如何初始化的。
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {// 此時 universe 包含所有的枚舉值Enum<?>[] universe = getUniverse(elementType);if (universe == null)throw new ClassCastException(elementType + " not an enum");// 根據(jù)枚舉元素的大小判斷初始化什么類型的 EnumSet 對象if (universe.length <= 64)return new RegularEnumSet<>(elementType, universe);elsereturn new JumboEnumSet<>(elementType, universe);}在 noneOf 方法中首先根據(jù)枚舉類型獲取所有的枚舉值,接著判斷然后根據(jù)枚舉類型的元素個數(shù)初始化對應(yīng)的 EnumSet 對象。
getUniverse 方法如下:
private static <E extends Enum<E>> E[] getUniverse(Class<E> elementType) {return SharedSecrets.getJavaLangAccess().getEnumConstantsShared(elementType);}關(guān)于 SharedSecrets,Java 官方文檔是這么描述的:
A repository of “shared secrets”, which are a mechanism for calling implementation-private methods in another package without using reflection.
意思是在不使用反射的情況下在另外的包中調(diào)用實現(xiàn)私有方法的機制。而 SharedSecrets.getJavaLangAccess().getEnumConstantsShared 方法用于獲取指定類型的枚舉元素數(shù)組。
2.2 add 方法
下面我們以 RegularEnumSet 為例來看下具體源碼的實現(xiàn)細(xì)節(jié)。
public boolean add(E e) {// 類型檢查typeCheck(e);// 獲取存儲枚舉元素的標(biāo)記值(比特位存儲了對應(yīng)的枚舉元素)long oldElements = elements;// 通過 | 運算在比特位上追加新元素elements |= (1L << ((Enum<?>)e).ordinal());// 如果枚舉元素已經(jīng)存在時,返回 falsereturn elements != oldElements;}add 方法的內(nèi)部實現(xiàn)就區(qū)區(qū)的四行代碼,下面我們來詳細(xì)分析下。首先進(jìn)行枚舉類型檢查,接著用一個 long 類型的整數(shù)值接收 elements,下面是 elements 在 RegularEnumSet 中的定義:
/*** Bit vector representation of this set. The 2^k bit indicates the* presence of universe[k] in this set.** 該數(shù)的二進(jìn)制如果比特位為 1,則表示有枚舉類型元素*/private long elements = 0L;在 RegularEnumSet 中使用的是一個 long 整數(shù)(二進(jìn)制)來表示是否添加或者刪除了某個枚舉值。添加枚舉元素的關(guān)鍵代碼是這句:elements |= (1L << ((Enum<?>)e).ordinal());,下面我們以一個例子來說明是如何把枚舉元素添加到集合的。
我們還以上面的 Season 枚舉為例,假設(shè)此時沒有添加任何元素則 elements 的值為 0,現(xiàn)在往集合里添加元素 FAIL,e.ordinal() 的值為 2,1 右移 2 位值為 4,換成二進(jìn)制表示為 0000 0100。如果 elements 用二進(jìn)制表示且某下標(biāo)(index)的值為 1,則表示該位上有枚舉元素,則枚舉元素就是 e.ordinal() = index 位置上的。
再舉個例子驗證一下,假設(shè)集合中已經(jīng)有了 FAIL 元素則 elements 的值為 4(0000 0100),現(xiàn)在添加元素 WINTER,e.ordinal() 的值為 3,1 右移 3 位值為 8,換成二進(jìn)制表示為 0000 1000,與 elements 進(jìn)行或運算后是(0000 1100)。
2.3 remove 方法
public boolean remove(Object e) {// 如果為 null,直接返回 falseif (e == null)return false;Class<?> eClass = e.getClass();// 類型檢查if (eClass != elementType && eClass.getSuperclass() != elementType)return false;// 獲取標(biāo)記值long oldElements = elements;// 將 remove 的元素對應(yīng)的比特位上的 1 置 0elements &= ~(1L << ((Enum<?>)e).ordinal());return elements != oldElements;}我們已經(jīng)知道了添加元素的過程,那移除元素?zé)o非就是把對應(yīng)位置上的 1 替換為 0,elements &= ~(1L << ((Enum<?>)e).ordinal()); 這行代碼執(zhí)行的就是這個邏輯,我們還以一個例子來說明下問題。
假設(shè)現(xiàn)在集合中有 FAIL 與 WINTER,則 elements 的值為 12(0000 1100),現(xiàn)在我們要移除 FAIL,(1L << ((Enum<?>)e).ordinal()) 運算過后值為 4(0000 0100),取反過后為 1111 1011,與 0000 1100 進(jìn)行與運算之后為 0000 1000,最后的結(jié)果可以看出,FAIL 對應(yīng)位置上的 1 經(jīng)過運算之后變成了 0。
RegularEnumSet 內(nèi)部的其他方法也是基于 elements 變量的二進(jìn)制進(jìn)行操作的,有興趣的可以自行查看。
總結(jié)
以上是生活随笔為你收集整理的Java8 EnumSet 源码简单分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 课程设计包括哪些内容(课程设计包括哪几部
- 下一篇: 触漫登录入口(触漫网页版登录)