最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java8的EnumMap源碼分析

 更新時(shí)間:2023年11月08日 11:19:07   作者:留蘭香丶  
這篇文章主要介紹了Java8的EnumMap源碼分析,EnumMap 是一個(gè)用于存儲(chǔ) key 為枚舉類(lèi)型的 map,底層使用數(shù)組實(shí)現(xiàn)(K,V 雙數(shù)組),與其他類(lèi)型 map 不同的是 EnumMap 底層使用雙數(shù)組來(lái)存儲(chǔ) key 與 value,key 數(shù)組會(huì)在構(gòu)造函數(shù)中根據(jù) keyType 進(jìn)行初始化,需要的朋友可以參考下

一、EnumMap 概述

EnumMap 是一個(gè)用于存儲(chǔ) key 為枚舉類(lèi)型的 map,底層使用數(shù)組實(shí)現(xiàn)(K,V 雙數(shù)組)。下面是其繼承結(jié)構(gòu):

public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>    implements java.io.Serializable, Cloneable

從上面的繼承結(jié)構(gòu)上可以看出 EnumMap 的 key 必須是一個(gè)枚舉類(lèi)型,而 value 沒(méi)有限制。

1.1 內(nèi)部屬性

    // key 類(lèi)型
    private final Class<K> keyType;
    // key 數(shù)組
    private transient K[] keyUniverse;
    // value 數(shù)組
    private transient Object[] vals;
    // 鍵值對(duì)個(gè)數(shù)
    private transient int size = 0;
    // value 為 null 時(shí)對(duì)應(yīng)的值
    private static final Object NULL = new Object() {
        public int hashCode() {
            return 0;
        }
        public String toString() {
            return "java.util.EnumMap.NULL";
        }
    };

與其他類(lèi)型 map 不同的是 EnumMap 底層使用雙數(shù)組來(lái)存儲(chǔ) key 與 value,key 數(shù)組會(huì)在構(gòu)造函數(shù)中根據(jù) keyType 進(jìn)行初始化,下面我們會(huì)看到。當(dāng) EnmumMap 的 value 為 null 時(shí)會(huì)特殊處理為一個(gè) Object 對(duì)象。

1.2 構(gòu)造函數(shù)

EnumMap 共提供了 3 個(gè)構(gòu)造函數(shù),如下:

在這里插入圖片描述

下面我們只來(lái)看其中一個(gè)指定類(lèi)型的構(gòu)造函數(shù)。

    public EnumMap(Class<K> keyType) {
        this.keyType = keyType;
        // 初始化 key 數(shù)組,getKeyUniverse 方法會(huì)計(jì)算出枚舉元素的總數(shù)并初始化 key 數(shù)組
        keyUniverse = getKeyUniverse(keyType);
        // 初始化 value 數(shù)組大小
        vals = new Object[keyUniverse.length];
    }

在使用上述構(gòu)造函數(shù)初始化 EnumMap 的時(shí)候必須指定枚舉類(lèi)型,上面我們已經(jīng)說(shuō)過(guò),EnumMap 會(huì)在構(gòu)造函數(shù)中初始化 key 數(shù)組,這個(gè)初始化動(dòng)作是在 getKeyUniverse(keyType) 中完成的。

    private static <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType) {
        return SharedSecrets.getJavaLangAccess()
                                        .getEnumConstantsShared(keyType);
    }

一開(kāi)始看上面的代碼可能有點(diǎn)懵,這怎么就初始化了 key 數(shù)組呢?在 Java 中我們可以通過(guò) JavaLangAccess 和 SharedSecrets 來(lái)獲取 JVM 中對(duì)象實(shí)例,具體是怎么實(shí)現(xiàn)的,有興趣的可以查相關(guān)的資料了解下。

我們以 debug 形式來(lái)驗(yàn)證下 key 數(shù)組是否會(huì)在構(gòu)造函數(shù)中被初始化與賦值:

首先來(lái)聲明一個(gè)枚舉類(lèi)型:

enum Season {
    SPRING("春天"), SUMMER("夏天"), FALL("秋天"), WINTER("冬天");

    private final String name;

    Season(String name) {
        this.name = name;
    }
}

測(cè)試類(lèi):

    public static void main(String[] args) throws Exception {
        EnumMap<Season, String> map = new EnumMap<>(Season.class);
    }

我們把斷點(diǎn)打在其構(gòu)造函數(shù)上就會(huì)看到 keyUniverse 數(shù)組被初始化了,且數(shù)組的元素順序與在枚舉類(lèi)型中定義的順序一致。如下圖:

在這里插入圖片描述

1.3 使用方式

    public static void main(String[] args) throws Exception {
        EnumMap<Season, String> map = new EnumMap<>(Season.class);
        map.put(Season.FALL, "碩果累累的秋天");
        map.put(Season.WINTER, "寒風(fēng)凜冽的冬天");
        System.out.println(map.get(Season.FALL));
    }

二、相關(guān)源碼分析

2.1 put 方法

    public V put(K key, V value) {
        // key 類(lèi)型檢查
        typeCheck(key);

        // 獲得該 key 對(duì)應(yīng)的位置
        int index = key.ordinal();
        // 在 vals 數(shù)組中獲取 key 角標(biāo)對(duì)應(yīng)的 value
        Object oldValue = vals[index];
        // 覆蓋或設(shè)置 value
        vals[index] = maskNull(value);
        // 如果 key 對(duì)應(yīng)的位置 value 為 null,則表示新插入了鍵值對(duì),size++,反之表示值覆蓋 size 不變
        if (oldValue == null)
            size++;
        return unmaskNull(oldValue);
    }

在添加鍵值對(duì)的時(shí)候會(huì)先檢查 key 的類(lèi)型,如果 key 的類(lèi)型不一致會(huì)拋出異常。

    private void typeCheck(K key) {
        Class<?> keyClass = key.getClass();
        if (keyClass != keyType && keyClass.getSuperclass() != keyType)
            throw new ClassCastException(keyClass + " != " + keyType);
    }

PS: keyType 在構(gòu)造函數(shù)中已經(jīng)被初始化了。

EnumMap 存儲(chǔ)鍵值對(duì)時(shí)并不會(huì)根據(jù) key 獲取對(duì)應(yīng)的哈希值,enum 本身已經(jīng)提供了一個(gè) ordinal() 方法,該方法會(huì)返回具體枚舉元素在枚舉類(lèi)中的位置(從 0 開(kāi)始),因此一個(gè)枚舉元素從創(chuàng)建就已經(jīng)有了一個(gè)唯一索引與其對(duì)應(yīng),這樣就不存在哈希沖突的問(wèn)題了。

如果添加的 value 為 null 會(huì)通過(guò) maskNull 方法特殊處理,存儲(chǔ)一個(gè) Object 對(duì)象。

    private Object maskNull(Object value) {
        return (value == null ? NULL : value);
    }

如果值覆蓋的話(huà),put 方法會(huì)返回舊的 value 值,并特殊處理 value 為 null 的情況:

    private V unmaskNull(Object value) {
        return (V)(value == NULL ? null : value);
    }

EnmuMap 添加鍵值對(duì)并沒(méi)有擴(kuò)容操作,因?yàn)橐粋€(gè)枚舉類(lèi)型到底有多少元素在代碼運(yùn)行階段是確定的,在構(gòu)造函數(shù)中已經(jīng)對(duì) key 數(shù)組進(jìn)行了初始化與賦值,value 數(shù)組的大小也已經(jīng)被確定。還有一個(gè)需要注意的問(wèn)題,在上面的 put 方法中只對(duì) value 進(jìn)行了處理,并沒(méi)有處理 key,原因就是 key 數(shù)組在構(gòu)造函數(shù)中已經(jīng)被賦值了。

2.2 remove 方法

     public V remove(Object key) {
        // key 類(lèi)型錯(cuò)誤的時(shí)候直接返回 null
        if (!isValidKey(key))
            return null;
        // 根據(jù) key 計(jì)算出其在枚舉中位置
        int index = ((Enum<?>)key).ordinal();
        // 獲取對(duì)應(yīng)的 value
        Object oldValue = vals[index];
        // value 置 null,下次 GC 回收
        vals[index] = null;
        // 如果對(duì)應(yīng)的 value 不為 null,如果添加鍵值對(duì)的時(shí)候 value 為 null,則存儲(chǔ)的是 NULL(Object)
        if (oldValue != null)
            size--;
        return unmaskNull(oldValue);
    }

在移除鍵值對(duì)的時(shí)候會(huì)先調(diào)用 isValidKey 方法對(duì) key 進(jìn)行一次檢查:

    private boolean isValidKey(Object key) {
        // key 為 null 直接返回 false
        if (key == null)
            return false;

        // Cheaper than instanceof Enum followed by getDeclaringClass
        Class<?> keyClass = key.getClass();
        // key 類(lèi)型檢查
        return keyClass == keyType || keyClass.getSuperclass() == keyType;
    }

remove 方法相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,這里就不總結(jié)了。

2.3 a question

從上面的源碼分析中我們知道,key 數(shù)組自從在構(gòu)造函數(shù)中完成初始化之后就沒(méi)有執(zhí)行過(guò)增刪改的操作,是不是意味著我們根據(jù)枚舉類(lèi)型創(chuàng)建一個(gè) EnumMap 之后,就算不添加任何鍵值對(duì),也能根據(jù)其迭代器獲取所有的 key,因?yàn)?key 在構(gòu)造函數(shù)中已經(jīng)被賦值了??聪旅娴拇a:

    public static void main(String[] args) throws Exception {
        EnumMap<Season, String> map = new EnumMap<>(Season.class);
        // 獲取迭代器對(duì)象
        Iterator<Map.Entry<Season, String>> iterator = map.entrySet().iterator();
        
        while (iterator.hasNext()) {
            System.out.println(iterator.next().getKey());
        }
    }

結(jié)果是上面的代碼并不會(huì)輸出任何 key,原因就在于 EnumMap 的 hasNext() 方法中對(duì) value 做了非空判斷,如下:

        public boolean hasNext() {
            // 循環(huán)中會(huì)略過(guò) value 數(shù)組中為 null 的情況
            while (index < vals.length && vals[index] == null)
                index++;
            return index != vals.length;
        }

盡管在構(gòu)造函數(shù)中 key 數(shù)組已經(jīng)被初始化,但是如果對(duì)應(yīng)的 value 為 null,在迭代的時(shí)候也會(huì)被過(guò)濾掉。

EnumMap 相對(duì)來(lái)說(shuō)比較簡(jiǎn)單,關(guān)于源碼就介紹到這里。

到此這篇關(guān)于Java8的EnumMap源碼分析的文章就介紹到這了,更多相關(guān)EnumMap源碼分析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 劍指Offer之Java算法習(xí)題精講鏈表與二叉樹(shù)專(zhuān)項(xiàng)訓(xùn)練

    劍指Offer之Java算法習(xí)題精講鏈表與二叉樹(shù)專(zhuān)項(xiàng)訓(xùn)練

    跟著思路走,之后從簡(jiǎn)單題入手,反復(fù)去看,做過(guò)之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化
    2022-03-03
  • java agent使用全解析

    java agent使用全解析

    這篇文章主要介紹了javaagent的相關(guān)資料,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解下
    2020-07-07
  • 解決Sentinel鏈路模式規(guī)則無(wú)效問(wèn)題

    解決Sentinel鏈路模式規(guī)則無(wú)效問(wèn)題

    本文介紹了如何在Spring Cloud Alibaba項(xiàng)目中使用Sentinel鏈路流控規(guī)則,并解決規(guī)則不生效的問(wèn)題,通過(guò)關(guān)閉Sentinel過(guò)濾器,可以避免重復(fù)統(tǒng)計(jì)請(qǐng)求
    2025-01-01
  • MyBatis-Plus 條件構(gòu)造器Wrapper使用示例詳解(最新整理)

    MyBatis-Plus 條件構(gòu)造器Wrapper使用示例詳解(最新整理)

    條件構(gòu)造器是 MyBatis-Plus 框架中用于動(dòng)態(tài)構(gòu)建 SQL 條件的核心工具類(lèi),通過(guò)鏈?zhǔn)骄幊谭绞綄?shí)現(xiàn)復(fù)雜查詢(xún)條件的組合,本文給大家介紹MyBatis-Plus條件構(gòu)造器Wrapper使用示例,感興趣的朋友一起看看吧
    2025-06-06
  • hadoop實(shí)現(xiàn)grep示例分享

    hadoop實(shí)現(xiàn)grep示例分享

    這篇文章主要介紹了hadoop實(shí)現(xiàn)grep示例,可從文檔中提取包含某些字符串的行,需要的朋友可以參考下
    2014-03-03
  • Java進(jìn)程CPU使用率過(guò)高排查步驟詳細(xì)講解

    Java進(jìn)程CPU使用率過(guò)高排查步驟詳細(xì)講解

    這篇文章主要介紹了Java進(jìn)程CPU使用率過(guò)高排查的相關(guān)資料,針對(duì)Java進(jìn)程CPU使用率高的問(wèn)題,我們可以遵循以下步驟進(jìn)行排查和優(yōu)化,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-06-06
  • 基于Java ActiveMQ的實(shí)例講解

    基于Java ActiveMQ的實(shí)例講解

    下面小編就為大家?guī)?lái)一篇基于Java ActiveMQ的實(shí)例講解。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09
  • spring之SpEL表達(dá)式詳解

    spring之SpEL表達(dá)式詳解

    這篇文章主要介紹了spring之SpEL表達(dá)式詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • JDK自帶的序列化方式優(yōu)缺點(diǎn)及實(shí)現(xiàn)原理面試精講

    JDK自帶的序列化方式優(yōu)缺點(diǎn)及實(shí)現(xiàn)原理面試精講

    這篇文章主要為大家介紹了JDK自帶的序列化方式優(yōu)缺點(diǎn)及實(shí)現(xiàn)原理面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • spring?boot項(xiàng)目中集成rocketmq詳細(xì)步驟

    spring?boot項(xiàng)目中集成rocketmq詳細(xì)步驟

    這篇文章主要給大家介紹了關(guān)于spring?boot項(xiàng)目中集成rocketmq的相關(guān)資料,springboot集成rocketmq的方法非常簡(jiǎn)單,文中直接上代碼,需要的朋友可以參考下
    2023-09-09

最新評(píng)論

光山县| 内江市| 徐水县| 松桃| 鸡西市| 泰兴市| 青浦区| 扎赉特旗| 京山县| 喀喇沁旗| 香格里拉县| 丹棱县| 莱西市| 基隆市| 新安县| 吕梁市| 贵港市| 偏关县| 云龙县| 孙吴县| 盐池县| 黑龙江省| 波密县| 红桥区| 松滋市| 佛坪县| 买车| 海盐县| 海原县| 义乌市| 烟台市| 新化县| 宁河县| 尼勒克县| 珠海市| 华坪县| 布拖县| 都匀市| 普格县| 吉首市| 日照市|