Java集合框架:EnumMap
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;
}
@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<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的使用 》
Java集合框架:EnumMap的更多相关文章
- Java集合框架总结
java集合框架主要分为实现了Collection接口的List和Set.映射接口Map. |-- List 有序,元素都有索引,可重复. |-- Set 无序,不可以存储重复的元素. |-- Map ...
- java集合框架容器 java框架层级 继承图结构 集合框架的抽象类 集合框架主要实现类
本文关键词: java集合框架 框架设计理念 容器 继承层级结构 继承图 集合框架中的抽象类 主要的实现类 实现类特性 集合框架分类 集合框架并发包 并发实现类 什么是容器? 由一个或多个确 ...
- Java 集合框架(常用数据结构)
早在Java 2中之前,Java就提供了特设类.比如:向量(Vector).栈(Stack).字典(Dictionary).哈希表(Hashtable)这些类(数据结构)用来存储和操作对象组.虽然这些 ...
- Java集合框架List,Map,Set等全面介绍
Java集合框架的基本接口/类层次结构: java.util.Collection [I]+--java.util.List [I] +--java.util.ArrayList [C] +- ...
- Java集合框架练习-计算表达式的值
最近在看<算法>这本书,正好看到一个计算表达式的问题,于是就打算写一下,也正好熟悉一下Java集合框架的使用,大致测试了一下,没啥问题. import java.util.*; /* * ...
- 【集合框架】Java集合框架综述
一.前言 现笔者打算做关于Java集合框架的教程,具体是打算分析Java源码,因为平时在写程序的过程中用Java集合特别频繁,但是对于里面一些具体的原理还没有进行很好的梳理,所以拟从源码的角度去熟悉梳 ...
- Java 集合框架
Java集合框架大致可以分为五个部分:List列表,Set集合.Map映射.迭代器.工具类 List 接口通常表示一个列表(数组.队列.链表 栈),其中的元素 可以重复 的是:ArrayList 和L ...
- Java集合框架之map
Java集合框架之map. Map的主要实现类有HashMap,LinkedHashMap,TreeMap,等等.具体可参阅API文档. 其中HashMap是无序排序. LinkedHashMap是自 ...
- 22章、Java集合框架习题
1.描述Java集合框架.列出接口.便利抽象类和具体类. Java集合框架支持2种容器:(1) 集合(Collection),存储元素集合 (2)图(Map),存储键值对.
- Java集合框架实现自定义排序
Java集合框架针对不同的数据结构提供了多种排序的方法,虽然很多时候我们可以自己实现排序,比如数组等,但是灵活的使用JDK提供的排序方法,可以提高开发效率,而且通常JDK的实现要比自己造的轮子性能更优 ...
随机推荐
- python 模块之platform模块(基本了解)
# -*- coding: cp936 -*-#python 27#xiaodeng#python 模块之platform模块(基本了解)#获取底层平台的识别数据 #知道该模块用途即可,需要使用时直接 ...
- Qt5.9静态库编译VS2015-x64
不多说. 编译配置参数如下 configure.bat -static -no-openssl -release 不支持OpenSSL,也没有安装各个数据库的Driver,所以数据库方面也只支持了SQ ...
- 编程填空:左边i位取反
总时间限制: 1000ms 内存限制: 1024kB描述写出函数中缺失的部分,使得函数返回值为一个整数,该整数的左边i位是n的左边i位取反,其余位和n相同请使用[一行代码]补全bitManipulat ...
- taskkill命令应用
taskkill命令用来在控制台下杀死进程 举例: 杀死PID为4276的进程 E:\android-sdk-windows\tools>taskkill /PID 4276 错误: 无法终止 ...
- JavaMail 接收邮件及删除
解析读取收件箱中邮件: import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io. ...
- ios中UIWebview中加载本地文件
[super viewDidLoad]; webview=[[UIWebView alloc] initWithFrame:self.view.bounds]; [self.view addSubvi ...
- h2database轻量级数据库
h2和derby一样,都是轻量级数据库,h2比derby还要轻巧,核心jar包不到1M.性能效率等等方面都非常好(前提不是高并发,高数据量) 轻量级数据库的应用出其不意,可以完成很多以前常规开发中无法 ...
- xshell连接centos vi编辑器不能使用小键盘
莫名其妙的本人的xshell,在vim编辑脚本的时候,右侧小键盘无法输出数字,输出的都是一些字母,而且还是一个字母占一行,太过艹蛋! 后来就按照正规的操作步骤,又重建了连接,居然发现正常了! 百思一虑 ...
- 通过Stetho在Chrome上调试Android App
引 入依赖包 compile 'com.facebook.stetho:stetho:1.3.1' 初始化一下 public class MyApplication extends Applicati ...
- 面试必备:HashMap源码解析(JDK8)
1 概述 本文将从几个常用方法下手,来阅读HashMap的源码. 按照从构造方法->常用API(增.删.改.查)的顺序来阅读源码,并会讲解阅读方法中涉及的一些变量的意义.了解HashMap的特点 ...