日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java8 EnumSet 源码简单分析

發(fā)布時間:2024/9/30 java 37 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java8 EnumSet 源码简单分析 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

一、EnumSet 概述

EnumSet 是一個用于存儲枚舉類型的 set 集合,一般的 set 集合底層都是使用對應的 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 }

輸出結果:

[SPRING, SUMMER, FAIL, WINTER]

[SPRING]

[SPRING, FAIL]

[SPRING, SUMMER, FAIL]

[WINTER]

一般我們不使用構造函數(shù)來直接定義 EnumSet,EnumSet 提供了很靈活的方法可以供使用者有選擇的進行初始化。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ù)初始化對應的 EnumSet 對象。

getUniverse 方法如下:

private static <E extends Enum<E>> E[] getUniverse(Class<E> elementType) {return SharedSecrets.getJavaLangAccess().getEnumConstantsShared(elementType);}

關于 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)細節(jié)。

public boolean add(E e) {// 類型檢查typeCheck(e);// 獲取存儲枚舉元素的標記值(比特位存儲了對應的枚舉元素)long oldElements = elements;// 通過 | 運算在比特位上追加新元素elements |= (1L << ((Enum<?>)e).ordinal());// 如果枚舉元素已經(jīng)存在時,返回 falsereturn elements != oldElements;}

add 方法的內(nèi)部實現(xiàn)就區(qū)區(qū)的四行代碼,下面我們來詳細分析下。首先進行枚舉類型檢查,接著用一個 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ù)的二進制如果比特位為 1,則表示有枚舉類型元素*/private long elements = 0L;

在 RegularEnumSet 中使用的是一個 long 整數(shù)(二進制)來表示是否添加或者刪除了某個枚舉值。添加枚舉元素的關鍵代碼是這句:elements |= (1L << ((Enum<?>)e).ordinal());,下面我們以一個例子來說明是如何把枚舉元素添加到集合的。

我們還以上面的 Season 枚舉為例,假設此時沒有添加任何元素則 elements 的值為 0,現(xiàn)在往集合里添加元素 FAIL,e.ordinal() 的值為 2,1 右移 2 位值為 4,換成二進制表示為 0000 0100。如果 elements 用二進制表示且某下標(index)的值為 1,則表示該位上有枚舉元素,則枚舉元素就是 e.ordinal() = index 位置上的。

再舉個例子驗證一下,假設集合中已經(jīng)有了 FAIL 元素則 elements 的值為 4(0000 0100),現(xiàn)在添加元素 WINTER,e.ordinal() 的值為 3,1 右移 3 位值為 8,換成二進制表示為 0000 1000,與 elements 進行或運算后是(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;// 獲取標記值long oldElements = elements;// 將 remove 的元素對應的比特位上的 1 置 0elements &= ~(1L << ((Enum<?>)e).ordinal());return elements != oldElements;}

我們已經(jīng)知道了添加元素的過程,那移除元素無非就是把對應位置上的 1 替換為 0,elements &= ~(1L << ((Enum<?>)e).ordinal()); 這行代碼執(zhí)行的就是這個邏輯,我們還以一個例子來說明下問題。

假設現(xiàn)在集合中有 FAIL 與 WINTER,則 elements 的值為 12(0000 1100),現(xiàn)在我們要移除 FAIL,(1L << ((Enum<?>)e).ordinal()) 運算過后值為 4(0000 0100),取反過后為 1111 1011,與 0000 1100 進行與運算之后為 0000 1000,最后的結果可以看出,FAIL 對應位置上的 1 經(jīng)過運算之后變成了 0。

RegularEnumSet 內(nèi)部的其他方法也是基于 elements 變量的二進制進行操作的,有興趣的可以自行查看。

總結

以上是生活随笔為你收集整理的Java8 EnumSet 源码简单分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。