EnumMap

EnumMap 能解决什么问题?什么时候使用 EnumMap?

1)EnumMap【枚举映射】中的键值必须来自单个枚举。
2)EnumMap 根据枚举键的自然顺序来维护,迭代遍历是有序的。
3)不允许使用 null 键,允许使用 null 值,内部会进行 mask 处理。
4)EnumMap 的键和值都通过对象数组来存储,读写性能高。

如何使用 EnumMap?

1)对象间映射关系的键来自单个枚举时,可以使用 EnumMap。

使用 EnumMap 有什么风险?

1)键和值的长度是根据枚举类型的元素个数确定的,可能存在一定的内存浪费。

EnumMap 核心操作的实现原理?

  • 创建实例
    /**
* 枚举键的 Class 类型
*/
private final Class<K> keyType; /**
* 枚举键的所有有效枚举值组成的数组
*/
private transient K[] keyUniverse; /**
* 底层存储值的对象数组
*/
private transient Object[] vals; /**
* 映射总数
*/
private transient int size = 0; /**
* 通过指定的键枚举类型创建空的枚举映射
*/
public EnumMap(Class<K> keyType) {
this.keyType = keyType;
// 缓存所有的枚举值数组
keyUniverse = EnumMap.getKeyUniverse(keyType);
// 创建存储值的对象数组
vals = new Object[keyUniverse.length];
}
  • 添加元素
    private static final Object NULL = new Object() {
@Override
public int hashCode() {
return 0;
} @Override
public String toString() {
return "java.util.EnumMap.NULL";
}
}; private Object maskNull(Object value) {
return value == null ? NULL : value;
} @SuppressWarnings("unchecked")
private V unmaskNull(Object value) {
return (V)(value == NULL ? null : value);
} /**
* 往枚举映射中添加元素
*/
@Override
public V put(K key, V value) {
typeCheck(key); // 获取键的自然顺序
final int index = key.ordinal();
// 读取旧值
final Object oldValue = vals[index];
// 写入新值
vals[index] = maskNull(value);
// 如果之前为该 slot 为空
if (oldValue == null) {
// 则增加计数值
size++;
}
// 返回 旧值
return unmaskNull(oldValue);
}
  • 读取元素
    /**
* 根据目标键读取值
*/
@Override
public V get(Object key) {
/**
* 1)键合法则读取指定索引处的值,并 mask 后返回
* 2)键非法则返回 null
*/
return isValidKey(key) ?
unmaskNull(vals[((Enum<?>)key).ordinal()]) : null;
} /**
* 校验键是否合法
*/
private boolean isValidKey(Object key) {
// 枚举映射的键不能为 null
if (key == null) {
return false;
} // Cheaper than instanceof Enum followed by getDeclaringClass
// 校验键的类型是否合法
final Class<?> keyClass = key.getClass();
return keyClass == keyType || keyClass.getSuperclass() == keyType;
}
  • 移除元素
    /**
* 1)如果指定的枚举键存在,则移除并返回旧值
* 2)否则返回 null
*/
@Override
public V remove(Object key) {
// 1)键非法则返回 null
if (!isValidKey(key)) {
return null;
}
// 读取枚举键的自然顺序
final int index = ((Enum<?>)key).ordinal();
// 读取旧值
final Object oldValue = vals[index];
// 置空
vals[index] = null;
if (oldValue != null) {
// 如果存在旧值,则递减元素总个数
size--;
}
return unmaskNull(oldValue);
}
  • 是否包含指定键
    /**
* 枚举映射包含指定的键
*/
@Override
public boolean containsKey(Object key) {
// 键合法并且以目标键的自然顺序为索引,读取 vals 中的值不为 null
return isValidKey(key) && vals[((Enum<?>)key).ordinal()] != null;
}
  • 是否包含指定值
    /**
* 值数组中是否包含指定的值
*/
@Override
public boolean containsValue(Object value) {
value = maskNull(value); for (final Object val : vals) {
if (value.equals(val)) {
return true;
}
} return false;
}

EnumMap 源码分析的更多相关文章

  1. Openrasp源码分析

    Openrasp是百度关于rasp技术的开源项目,由于工作需要,之前对rasp的源码进行了简单的分析.文章是之前就写好的,现在放出了,希望对大家有写帮助. OpenRASP中java引擎的源码分析 安 ...

  2. Springboot学习04-默认错误页面加载机制源码分析

    Springboot学习04-默认错误页面加载机制源码分析 前沿 希望通过本文的学习,对错误页面的加载机制有这更神的理解 正文 1-Springboot错误页面展示 2-Springboot默认错误处 ...

  3. JDK集合框架源码分析 - 简单概要

    1.类继承体系 在集合框架的类继承体系中,最顶层有两个接口Collection.Map: Collection 表示一组纯数据 Map 表示一组key-value对 Collection的类继承体系: ...

  4. ABP源码分析一:整体项目结构及目录

    ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...

  5. HashMap与TreeMap源码分析

    1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Ja ...

  6. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  7. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  8. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  9. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

随机推荐

  1. 2、数据类型和运算符——Java数据类型

    一.强类型语言和弱类型语言: 1.1 强类型语言 强类型语言是一种强制类型定义的语言,一旦某一个变量被定义类型,如果不经过强制转换,则它永远就是该数据类型了,强类型语言包括Java..net .Pyt ...

  2. 自动清理ES索引脚本

    #/bin/bash #指定日期(3个月前) DATA=`date -d "3 month ago" +%Y.%m.%d` #当前日期 time=`date` #删除3个月前的日志 ...

  3. MongoDB的使用学习之(五)Spring集成MongoDB以及简单的CRUD

    这篇文章不错:Spring Data - MongoDB 教程 (1.0.0.M1)http://miller-cn.iteye.com/blog/1258859 1.介绍 之前在很多地方一直见到这个 ...

  4. hashlib模块和logging模块

    hashlib Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等. 我们以常见的摘要算法MD5为例,计算出一个字符串的MD5值: import hashlib m=hashli ...

  5. 03python面向对象编程之Python中单下划线和双下划线的区别7

    通常Python类中会有_和__的方法,是指什么意思呢?如下: 双下划线表示内部不允许访问,一个下划线表示这样的实例变量外部是可以访问的,但是,按照约定俗成的规定,当你看到这样的变量时,意思就是,“虽 ...

  6. win10文件夹共享

    1.开启server服务就可以使用net share 命令 2.查看目前已共享的文件夹 3.关闭默认共享 只有用administrator(且有密码)才能连上win10上的默认共享了,只有admini ...

  7. L5 Swagger 使用说明

    网上看了看,关于这个扩展介绍很少.今天工作恰好用到,研究了一下,觉得有必要分享一下. 一.  简介: 这个包是Swagger-php和Swagger-ui的封装,适用于Laravel5. 二.版本要求 ...

  8. ThinkPHP生成静态页buildHtml方法

    原来ThinkPHP自带了生成静态页的函数buildHtml,使用起来很方便!最新的手册里没写这个方法,向大家介绍一下. PHP 1 2 3 4 5 6 7 8 9 10 11     protect ...

  9. Django【第15篇】:Django之Form组件归类

    Form组件归类 一.Form类 创建Form类时,主要涉及到 [字段] 和 [插件],字段用于对用户请求数据的验证,插件用于自动生成HTML; 1.Django内置字段如下: 1 Field 2 r ...

  10. 用selenium启动chrome浏览器

    python 3.7 pycharm 1.安装selenium pip3 install selenium 2.下载与chrome匹配的chromdriver.exe,放到项目的解释器路径下,跟pyt ...