EnumMap定义

package java.util;import java.util.Map.Entry;
import sun.misc.SharedSecrets;
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>implements java.io.Serializable, Cloneable{private final Class<K> keyType;private transient K[] keyUniverse;private transient Object[] vals;private transient int size = 0;
}

  keyType变量是EnumMap的key泛型的类对象,EnumMap依据这个类型。能够获得keyUniverse的内容。vals存放的是与keyUniverse映射的值。假设没有映射则为null,假设映射为null则会特殊处理成NULL。NULL的定义例如以下:

 private static final Object NULL = new Object() {public int hashCode() {return 0;}public String toString() {return "java.util.EnumMap.NULL";}};

  对于值NULL的处理相似WeakHashMap的特殊处理,会有两个方法:

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

  这样能够区分vals中是null(即没有映射)还是NULL(即映射为null);
  EnumMap的size是依据vals中的非null(包含NULL)的值的个数确定的,比方put方法:

    public V put(K key, V value) {typeCheck(key);int index = key.ordinal();Object oldValue = vals[index];vals[index] = maskNull(value);if (oldValue == null)size++;return unmaskNull(oldValue);}

  typeCheck推断key的类对象或者父类对象是否与keyType相等,假设不相等则抛出ClassCastException异常。
  注意EnumMap并没有相似HashMap的resize的过程,也没有载入因子的概念,由于在一个EnumMap创建的时候,keyUniverse和vals的大小就固定。


EnumMap使用

  先举个小样例:

package collections.map;import java.util.EnumMap;
import java.util.Map;public class EnumMapTest
{public enum Color{RED,BLUE,BLACK,YELLOW,GREEN;}public static void main(String[] args){EnumMap<Color,String> map = new EnumMap<>(Color.class);EnumMap<Color,String> map = new EnumMap<>(Color.class);map.put(Color.YELLOW, "黄色");map.put(Color.RED, "红色");map.put(Color.BLUE, null);
//        map.put(null, "无");   //会报NullPonitException的错误map.put(Color.BLACK, "黑色");map.put(Color.GREEN, "绿色");for(Map.Entry<Color,String> entry:map.entrySet()){System.out.println(entry.getKey()+":"+entry.getValue());}System.out.println(map);}
}

  执行结果:

RED:红色
BLUE:null
BLACK:黑色
YELLOW:黄色
GREEN:绿色
{RED=红色, BLUE=null, BLACK=黑色, YELLOW=黄色, GREEN=绿色}

  EnumMap的key不同意为null,value能够为null,依照key在enum中的顺序进行保存。非线程安全。能够用工具类Collections进行包装成线程安全的:

Map<EnumKey, V> m = Collections.synchronizedMap(new EnumMap<EnumKey, V>(...));

  有关enum的应用知识能够參考《Java枚举类型enum》。
  EnumMap的基本操作都比較快,都在常量时间内完毕,基本上(但不保证)比HashMap快。
  EnumMap有三个构造函数:

  • public EnumMap(Class<K> keyType);
  • public EnumMap(EnumMap<K, ?

    extends V> m);

  • public EnumMap(Map<K, ?

    extends V> m) ;

  前两个构造函数一目了然,对第三个构造函数进行分析:

Map<Integer,Integer> map1 = new HashMap<>();map1.put(1, 1);map1.put(3, 3);map1.put(2, 2);Map<Integer,Integer> map2 = new EnumMap<>(map1);//编译器提示错误:Cannot infer type arguments for EnumMap<>

  这个是由于Integer并非extends Enum;
  这里变换一下,採用Map

Map<Enum,Integer> map1 = new HashMap<>();map1.put(Color.YELLOW, 1);map1.put(Color.RED, 3);map1.put(Color.BLUE, 2);Map<Enum,Integer> map2 = new EnumMap<>(map1);for(Map.Entry entry:map2.entrySet()){System.out.println(entry.getKey()+":"+entry.getValue());}System.out.println(map2);System.out.println(map2.size());

  能够正常执行。输出结果:

RED:3
BLUE:2
YELLOW:1
{RED=3, BLUE=2, YELLOW=1}
3

  相信大家能够总结个一二了吧。


EnumMap用途

  《Effective Java》中作者建议用EnumMap取代叙述索引。最好不要用序数来索引数组,而要使用EnumMap
  这里採用《Effective Java》书中的样例来举例。

     public static class Herb{public enum Type{ANNUAL, PERENNIAL, BIENNTAL}private final String name;private final Type type;public Herb(String name, Type type){this.name = name;this.type = type;}public Type getType(){return type;}@Overridepublic String toString(){return name;}}

  如今用一座种满香草的花园,想要依照类型(一年生、多年生、两年生,即上面Type的类型)进行组织之后将这些植物列出来。假设使用数组实现的话。须要构建三个集合,每种类型一个。而且遍历整座花园,将每种香草放到相应的集合中。

Herb[] garden = new Herb[]{new Herb("f1",Herb.Type.ANNUAL),new Herb("f2",Herb.Type.PERENNIAL),new Herb("f3",Herb.Type.BIENNTAL),new Herb("f4",Herb.Type.PERENNIAL),new Herb("f5",Herb.Type.ANNUAL),new Herb("f6",Herb.Type.BIENNTAL),new Herb("f7",Herb.Type.ANNUAL),new Herb("f8",Herb.Type.BIENNTAL),new Herb("f9",Herb.Type.PERENNIAL)};Set<Herb>[] herbsByType = (Set<Herb>[]) new Set[Herb.Type.values().length];for(int i=0;i<herbsByType.length;i++){herbsByType[i] = new HashSet<Herb>();}for(Herb h:garden){herbsByType[h.type.ordinal()].add(h);}for(int i=0;i<herbsByType.length;i++){System.out.printf("%s:%s%n", Herb.Type.values()[i],herbsByType[i]);}

  执行结果:

ANNUAL:[f5, f7, f1]
PERENNIAL:[f4, f2, f9]
BIENNTAL:[f8, f3, f6]

  这样的方法确实可行。可是影藏着很多问题。由于数组不能和泛型兼容。程序须要进行未受检的转换,而且不能正确无误地进行编译。由于数组不知道它的索引代表着什么,你必须手工标注这些索引的输出。可是这样的方法最严重的问题在于。当你訪问一个依照枚举的叙述进行索引的数组时,使用正确的int值就是你的职责了。int不能提供枚举的类型安全。
  可是你能够用EnumMap改善这个程序:

Herb[] garden = new Herb[]{new Herb("f1",Herb.Type.ANNUAL),new Herb("f2",Herb.Type.PERENNIAL),new Herb("f3",Herb.Type.BIENNTAL),new Herb("f4",Herb.Type.PERENNIAL),new Herb("f5",Herb.Type.ANNUAL),new Herb("f6",Herb.Type.BIENNTAL),new Herb("f7",Herb.Type.ANNUAL),new Herb("f8",Herb.Type.BIENNTAL),new Herb("f9",Herb.Type.PERENNIAL)};Map<Herb.Type, Set<Herb>> herbsByType = new EnumMap<>(Herb.Type.class);for(Herb.Type t : Herb.Type.values()){herbsByType.put(t, new HashSet<Herb>());}for(Herb h:garden){herbsByType.get(h.type).add(h);}System.out.println(herbsByType);

  执行结果:

{ANNUAL=[f7, f1, f5], PERENNIAL=[f4, f2, f9], BIENNTAL=[f8, f6, f3]}

  这段程序更剪短、更清楚,也更安全。执行速度方面能够与使用序数的数组相媲美。注意EnumMap构造器採用键类型的Class对象:这是一个有限制的类型令牌,它提供了执行时的泛型信息


总结

  EnumMap是专门为枚举类型量身定做的Map实现。

尽管使用其他的Map实现(如HashMap)也能完毕枚举类型实例到值得映射,可是使用EnumMap会更加高效:它仅仅能接收同一枚举类型的实例作为键值。而且由于枚举类型实例的数量相对固定而且有限,所以EnumMap使用数组来存放与枚举类型相应的值。这使得EnumMap的效率很高。EnumMap在内部使用枚举类型的ordinal()得到当前实例的声明次序,并使用这个次序维护枚举类型实例相应值在数组的位置。


參考资料:
1. 《Java枚举类型enum》
2. 《Effective Java(Second Edition)》. Joshua Bloch.
3. 《EnumMap与Enumset的使用 》

转载于:https://www.cnblogs.com/yangykaifa/p/7388563.html

Java集合框架:EnumMap相关推荐

  1. java集合框架容器 java框架层级 继承图结构 集合框架的抽象类 集合框架主要实现类...

    本文关键词: java集合框架  框架设计理念  容器 继承层级结构 继承图 集合框架中的抽象类  主要的实现类 实现类特性   集合框架分类 集合框架并发包 并发实现类 什么是容器? 由一个或多个确 ...

  2. Java集合框架总结(5)——Map接口的使用

    Java集合框架总结(5)--Map接口的使用 Map用于保存具有映射关系的数据(key-vlaue).Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回fa ...

  3. Java集合框架:总结

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

  4. Java 集合框架分析:JAVA集合中的一些边边角角的知识

    相关文章: Java 集合框架分析:Set http://blog.csdn.net/youyou1543724847/article/details/52733723 Java 集合框架分析:Lin ...

  5. Java集合框架完全解析

    1.集合概述 现实生活中集合:很多事物凑在一起. 数学中的集合:具有共同属性的事物的总体. Java中的集合类:是一种工具类,就像是容器,储存任意数量的具有共同属性的对象.在编程时,常常需要集中存放多 ...

  6. Java集合框架源码详解系列(一)

     写在前面:大家好!我是晴空๓.如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正,感谢大家的不吝赐教.我的唯一博客更新地址是:https://ac-fun.blog.csdn.net/.非常 ...

  7. Java 集合框架系列,总结性全文,解决你所有困惑

    文章目录 集合接口 Collection Map 集合实现类 抽象类实现 通用实现 遗留实现 并发实现 特殊实现 适配器实现 包装器实现 便利实现 基础设施 算法和工具实现 定长/变长 可改/不可改 ...

  8. Java集合框架综述,这篇让你吃透!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:平凡希 cnblogs.com/xiaoxi/p/60899 ...

  9. 【Java集合框架】ArrayList类方法简明解析(举例说明)

    本文目录 1.API与Java集合框架 2.ArrayList类方法解析 2.1 add() 2.2 addAll() 2.3 clear() 2.4 clone() 2.5 contains() 2 ...

  10. Java集合框架的知识总结(1)

    Java集合框架的知识总结(1) 所有集合类都位于java.util包下.集合中只能保存对象(保存对象的引用变量). Java的集合类主要由两个接口派生而出:Collection和Map,Collec ...

最新文章

  1. 基于Linux(LAMP)平台搭建MYsql数据库(二)
  2. jQuery和AngularJS的区别小分析
  3. 算法题指南书_分类算法指南
  4. JavaScript时间日期函数
  5. python映射实体类_【HIBERNATE框架开发之二】第一个HIBERNATE-ANNONATION项目(采用@ENTITY、@ID直接映射实体类)...
  6. 利用anaconda prompt打开jupyter notebook
  7. php 面向对象问题,PHP 面向对象开发的一些问题
  8. matlab调用refprop9.0,Matlab调用REFPROP完全说明
  9. (ExcelVBA编程入门范例)
  10. “保镖”无人机出没,让坏人无处遁形
  11. 安卓图片轮播(banner)
  12. 6种时间管理方法概要
  13. RS485 光照度传感器 pc串口工具调试设备 亲身体验
  14. 深度报道 | 5G兵临城下:AIoT将面临哪些变革和挑战?
  15. 肝移植笔记2:论文阅读-肝移植辅助决策研究
  16. ansys怎么合并体_ANSYS梁体组合连接计算方法的讨论
  17. 基于Ardupilot/PX4固件,APM/PIXhawk硬件的VTOL垂直起降固定翼软硬件参数调试(第三篇)故障保护及问题诊断
  18. NOJ 1116 哈罗哈的大披萨 【淡蓝】 [状压dp+各种优化]
  19. mybatis使用foreach进行批量操作 The error may involve defaultParameterMap
  20. Substance Painter材质导入unity渲染通道配置更改

热门文章

  1. [C++对象模型][6]sizeof与对象内存布局
  2. 《OpenCV3编程入门》学习笔记7 图像变换(三 )重映射
  3. abb机器人指令手册_ABB机器人可以告别示教器啦
  4. java两个jre_为什么会有两个jre?060925(原创)
  5. c语言课程设计的摘要,投票程序设计-C语言课程设计摘要.doc
  6. python数据分析实训大纲,数据分析大赛考纲:(二)Python数据分析应会部分
  7. C++ 三五法则,看看你能不能理解
  8. Python访问街区10个点,并俩俩绘制一条线,得到5条线,求最短的距离和?
  9. 【camera-radar】相机-毫米波雷达联合标定方案介绍+实现
  10. 基于pytorch的卷积神经网络量化实现