EnumMap & EnumSet

一、EnumMap

一)基本用法

    public static Map<Size, Integer> countBySize(List<Clothes> clothes) {
EnumMap<Size, Integer> map = new EnumMap<>(Size.class);
for (Clothes c : clothes) {
Size size = c.getSize();
Integer count = map.get(size);
if (count != null) {
map.put(size, count + 1);
} else {
map.put(size, 1);
}
}
return map;
} public static void main(String[] args) {
List<Clothes> clothes = Arrays.asList(new Clothes[]{
new Clothes("C001",Size.SMALL), new Clothes("C002", Size.LARGE),
new Clothes("C003", Size.LARGE), new Clothes("C004", Size.MEDIUM),
new Clothes("C005", Size.SMALL), new Clothes("C006", Size.SMALL),
});
System.out.println(countBySize(clothes)); //{SMALL=3, MEDIUM=1, LARGE=2}
}

EnumMap是保证顺序的:输出是按照键在枚举中的顺序。

二)实现原理

EnumMap的实例变量:

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 = getKeyUniverse(keyType); //最终调用了枚举类型的values方法,该方法返回所有可能的枚举值
vals = new Object[keyUniverse.length];
}

保存键值对的put方法:

public V put(K key, V value) {
//检查键的类型
typeCheck(key);
//获取索引
int index = key.ordinal();
//放入值value
Object oldValue = vals[index];
//如果有值为null打包null值,这是为了区别null值与没有值
vals[index] = maskNull(value);
if(oldValue == null)
size++;
return unmaskNull(oldValue);
}
    private static final Object NULL = new Object() {
public int hashCode() {
return 0;
}
public String toString() {
return "java.util.EnumMap.NULL";
}
};
private Object maskNull(Object value) {
return (value == null ? NULL : value);
}
private V unmaskNull(Object value) {
return(V) (value == NULL ? null : value);
}

二、EnumSet

EnumSet的实现与EnumMap没有任何关系,而是用极为精简高效的位向量实现的。

位向量是计算机程序中解决问题的一种常用方式,我们有必要理解和掌握。

一)基本用法

通过EnumSet的静态工厂方法,可以创建EnumSet对象,比如:

//创建一个指定类型的EnumSet,不含任何元素,创建的EnumSet实际类型是EnumSet的子类
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType)

用法举例:

public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
EnumSet<Day> days = EnumSet.noneOf(Day.class);
days.add(Day.FRIDAY);
days.add(Day.SUNDAY);
days.add(Day.MONDAY);
System.out.println(days); //[MONDAY, FRIDAY, SUNDAY]

EnumSet的其他静态工厂方法:

//初始集合包括指定枚举类型的所有枚举值
<E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)
//初始集合包括枚举值中指定范围的元素
<E extends Enum<E>> EnumSet<E> range(E from, E to)
//包含指定集合的补集
<E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)
//包含参数中的所有元素
<E extends Enum<E>> EnumSet<E> of(E e)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)
<E extends Enum<E>> EnumSet<E> of(E first, E... rest)
//包含参数中的所有元素
<E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s)
<E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)

二、应用场景

Worker[] workers = new Worker[]{
new Worker("Tom", EnumSet.of(Day.FRIDAY, Day.MONDAY, Day.SATURDAY)),
new Worker("Jerry", EnumSet.of(Day.MONDAY, Day.WEDNESDAY, Day.THURSDAY)),
new Worker("Teddy", EnumSet.of(Day.TUESDAY, Day.FRIDAY, Day.WEDNESDAY))
};
//哪些天一个人都不会来
EnumSet<Day> allDays = EnumSet.allOf(Day.class);
for (Worker w : workers) {
allDays.remove(w.getWorkingDays());
}

三、实现原理

位向量:用一个位表示一个元素状态,用一组位表示集合状态,

每个位对应一个元素,而状态只可能有两种。

比如之前的枚举类Day,它有7个枚举值,一个Day的集合就可以用一个字节byte表示,

最高位不用,设为0,从右到左,每位对应一个枚举值,1表示包含该元素,0表示不包含该元素。

EnumSet的实现,首先是主要实例变量:

final Class<E> elementType; //表示类型信息
final Enum[] universe; //表示所有枚举值

EnumSet自身没有记录元素个数的变量,也没有位向量,它们是子类维护的。

对于RegularEnumSet,它用一个long类型表示位向量,代码为:

private long elements = 0L;

RegularEnumSet没有定义元素个数的变量,都是实时计算出来的:

public int size() {
return Long.bitCount(elements);
}

而对于JumboEnumSet,用一个long数组表示,有单独的size变量

private long elements[];
private int size = 0;

EnumSet的静态工厂方法:

public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum<?>[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
}

RegularEnum 和 SetJumboEnumSet的构造方法:

RegularEnumSet(Class<E>elementType, Enum[] universe) {
super(elementType, universe);
}
JumboEnumSet(Class<E>elementType, Enum[] universe) {
super(elementType, universe);
elements = new long[(universe.length + 63) >>> 6];
}
EnumSet(Class<E>elementType, Enum[] universe) {
this.elementType = elementType;
this.universe = universe;
}

其他源码略。

四、实现原理

对于只有两种状态,且需要进行集合运算的数据,使用位向量表示

、位运算进行处理,是计算机程序中一种常见的思维方式。

Java笔记(九)EnumMap & EnumSet的更多相关文章

  1. Java笔记(九)……面向对象I

    面向对象概念 理解面向对象 面向对象是相对面向过程而言 面向对象和面向过程都是一种思想 面向过程 强调的是功能行为 面向对象 将功能封装进对象,强调具备了功能的对象. 面向对象是基于面向过程的. 面向 ...

  2. Elasticsearch笔记九之优化

    Elasticsearch笔记九之优化 ).get(); } curl命令可以在linux中建立一个定时任务每天执行一次,同样java代码也可以建立一个定时器来执行. 2:内存设置之前介绍过es集群有 ...

  3. 多线程学习笔记九之ThreadLocal

    目录 多线程学习笔记九之ThreadLocal 简介 类结构 源码分析 ThreadLocalMap set(T value) get() remove() 为什么ThreadLocalMap的键是W ...

  4. 《Java笔记——基础语法》

    Java笔记--基础语法       一.字符串的拼接: 例如: System.out.println(""+"");     二.换行语句: 例如: Syst ...

  5. Effective Java笔记一 创建和销毁对象

    Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...

  6. 《MFC游戏开发》笔记九 游戏中的碰撞判定初步&怪物运动简单AI

    本系列文章由七十一雾央编写,转载请注明出处. http://blog.csdn.net/u011371356/article/details/9374935 作者:七十一雾央 新浪微博:http:// ...

  7. java笔记00-目录

    --2013年7月26日17:49:59 学习java已久,趁最近有空,写一个总结: java笔记01-反射:

  8. Java笔记:Java集合概述和Set集合

    本文主要是Java集合的概述和Set集合 1.Java集合概述 1)数组可以保存多个对象,但数组长度不可变,一旦在初始化数组时指定了数组长度,这个数组长度就是不可变的,如果需要保存数量变化的数据,数组 ...

  9. java笔记整理

    Java 笔记整理 包含内容     Unix Java 基础, 数据库(Oracle jdbc Hibernate pl/sql), web, JSP, Struts, Ajax Spring, E ...

随机推荐

  1. Java在线备份和还原MySQL数据库。

    2018年6月29日14:00:48 阅读数:1534 今天整了整整一整天,终于使用Java在线备份和还原MySQL数据库了,哎,备份倒是很快,就是在还原的时候遇到了一个问题,也不报错,结果将sql语 ...

  2. Oracle下载 OPatch

    今天被朋友问及,如何下载OPatch ...我当时有些凌乱的.事后想想,人与人的思维是不同的,对待同一个问题,有人觉得很简单,有人觉得无从下手 . 乱不多说了.开始说明下吧. 1. 首先要有一个MOS ...

  3. JDK1.8 JVM参数配置

    JAVA_OPTS=" -server #服务器模式 -Xmx4g #JVM最大允许分配的堆内存,按需分配 -Xms4g #JVM初始分配的堆内存,一般和Xmx配置成一样以避免每次gc后JV ...

  4. vsftp虚拟主机实战

    文件传输协议,基于该协议FTP客户端与服务端可以实现共享文件,上传文件,下载文件.FTP基于TVP协议生成一个虚拟的连接,主要用于控制FTP连接信息,同时再生成一个单独的TCP连接用于FTP数据传输, ...

  5. Flask--Web From 表单

    需要拓展支持 1.Flask-WTF是WTForms项目的Flask框架扩展,我们将用他来帮助我们处理web表单 2.pip install Flask-WTF 配置WTF(不知道为什么,但是好像必须 ...

  6. Codeforces 1017F The Neutral Zone 数论

    原文链接https://www.cnblogs.com/zhouzhendong/p/CF1017F.html 题目传送门 - CF1017F 题意 假设一个数 $x$ 分解质因数后得到结果 $x=p ...

  7. HDU4622 Reincarnation 字符串 SAM

    原文链接https://www.cnblogs.com/zhouzhendong/p/HDU4622.html 题目传送门 - HDU4622 题意 多组数据. 对于每一组数据,给定一个字符串 s , ...

  8. BZOJ2534 Uva10829L-gap字符串 字符串 SA ST表

    原文链接https://www.cnblogs.com/zhouzhendong/p/9240665.html 题目传送门 - BZOJ2534 题意 有一种形如 $uvu$ 形式的字符串,其中 $u ...

  9. CF552 E. Two Teams

    题意:给出一串n个数   为1-n的乱序 一共有两个教练   教练一的队伍是1队  二是二队 教练一选择 当前队列中剩余人数的最大序号   将其和左边k个人 和右边k个人 变为一队 如此反复直到所有人 ...

  10. 011 SpringSecurity的基本原理

    一:securuty默认情况 1.默认的配置 在引用security依赖以后,会有一个配置 security.basic.enabled=true 2.启动 用户名:user 密码:在控制台上查看 3 ...