目录(?)[+]
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的大小就固定。
先举个小例子:
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有三个构造函数:
前两个构造函数一目了然,对第三个构造函数进行分析:
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<></span>
这个是因为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
《Effective Java》中作者建议用EnumMap代替叙述索引,最好不要用序数来索引数组,而要使用EnumMap。
注意EnumMap构造器采用键类型的Class对象:这是一个有限制的类型令牌,它提供了运行时的泛型信息。
EnumMap是专门为枚举类型量身定做的Map实现。虽然使用其它的Map实现(如HashMap)也能完成枚举类型实例到值得映射,但是使用EnumMap会更加高效:它只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以EnumMap使用数组来存放与枚举类型对应的值。这使得EnumMap的效率非常高。EnumMap在内部使用枚举类型的ordinal()得到当前实例的声明次序,并使用这个次序维护枚举类型实例对应值在数组的位置。
本文转载自:点击打开链接
参考资料:
1. 《Java枚举类型enum》
2. 《Effective Java(Second Edition)》. Joshua Bloch.
3. 《EnumMap与Enumset的使用 》