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;
}

最新文章

  1. ObjectOutputStream和ObjectInputStream
  2. 圆内,求离圆心最远的整数点 hiho一下第111周 Farthest Point
  3. Javascript中的迭代、归并方法
  4. Silverlight中在MVVM模式下对DatagridRow选择控件封装
  5. Java---基于TCP协议的相互即时通讯小程序
  6. NOI2013 快餐店
  7. Api之Cors跨域以及其他跨域方式
  8. 第23章 访问者模式(Visitor Pattern)
  9. Failed to load c++ bson extension, using pure JS version
  10. &amp; 与 kill -3
  11. 【转】&lt;string&gt; &lt;string.h&gt; &lt;cstring&gt;的区别
  12. 浅谈轮播图(原生JavaScript实现)
  13. android驱动例子(LED灯控制)
  14. Visual Studio Code(VSCODE)语言设置
  15. tp5框架中jquery+ajax分页
  16. DataBase(28)
  17. 魔方Newlife.Cube权限系统的使用及模版覆盖详解
  18. 通过Roslyn构建自己的C#脚本(更新版)(转)
  19. hdu 1254 推箱子(双重bfs)
  20. Hive SQL的编译过程

热门文章

  1. read、readline 和 readlines 的区别?
  2. Linux--Linux的网络--05
  3. Python自动化学习--元素定位
  4. tf中的meta和pb
  5. 将pip源更换到国内镜像
  6. Ansible笔记(2)--配置清单
  7. .gitignore配置文件
  8. Java语言基础1-关键字、标识符、常量和变量
  9. PHP培训教程 php生成WAP页面
  10. Android开发实践:Android.mk模板