博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java集合框架:EnumMap
阅读量:6327 次
发布时间:2019-06-22

本文共 6311 字,大约阅读时间需要 21 分钟。

EnumMap定义

package java.util;import java.util.Map.Entry;import sun.misc.SharedSecrets;public class EnumMap
, V> extends AbstractMap
implements java.io.Serializable, Cloneable{
private final Class
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
map = new EnumMap<>(Color.class); EnumMap
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
entry:map.entrySet()) { System.out.println(entry.getKey()+":"+entry.getValue()); } System.out.println(map); }}

  执行结果:

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

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

Map
m = Collections.synchronizedMap(new EnumMap
(...));

  有关enum的应用知识能够參考《》。

  EnumMap的基本操作都比較快,都在常量时间内完毕,基本上(但不保证)比HashMap快。
  EnumMap有三个构造函数:

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

    extends V> m);

  • public EnumMap(Map<K, ?

    extends V> m) ;

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

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

  这个是由于Integer并非extends Enum;

  这里变换一下,採用Map

Map
map1 = new HashMap<>(); map1.put(Color.YELLOW, 1); map1.put(Color.RED, 3); map1.put(Color.BLUE, 2); Map
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:3BLUE:2YELLOW: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; } @Override public 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
[] herbsByType = (Set
[]) new Set[Herb.Type.values().length]; for(int i=0;i
(); } for(Herb h:garden) { herbsByType[h.type.ordinal()].add(h); } for(int i=0;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
> herbsByType = new EnumMap<>(Herb.Type.class); for(Herb.Type t : Herb.Type.values()) { herbsByType.put(t, new HashSet
()); } 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. 《》
2. 《Effective Java(Second Edition)》. Joshua Bloch.
3. 《 》

转载地址:http://yxgaa.baihongyu.com/

你可能感兴趣的文章
C# mongodb 类库
查看>>
系统调用(一)
查看>>
WCF 设计和实现服务协定(01)
查看>>
【原】东拼西凑PBR(1):PBR基础
查看>>
react 从零开始搭建开发环境
查看>>
scala recursive value x$5 needs type
查看>>
ps -ef |grep 输出的具体含义
查看>>
markdown编辑
查看>>
【转】什么是“对用户友好”
查看>>
ASCII 在线转换器
查看>>
Linux内核同步:RCU
查看>>
Android逆向进阶——让你自由自在脱壳的热身运动(dex篇)
查看>>
Java设计模式之五大创建型模式(附实例和详解)
查看>>
60 Permutation Sequence
查看>>
主流的RPC框架有哪些
查看>>
Hive学习之路 (七)Hive的DDL操作
查看>>
[转]mysql使用关键字作为列名的处理方式
查看>>
awesome go library 库,推荐使用的golang库
查看>>
树形展示形式的论坛
查看>>
jdbcTemplate 调用存储过程。 入参 array 返回 cursor
查看>>