有时候,会见到利用ordinal方法来索引数组的代码。例如下面这个简化的类,表示一种烹饪用的香草:

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

假设有一个香草的数组,表示一座花园中的植物,想要按照类型(一年生、多年生或者两年生植物)进行组织后将植物列出来。

1.将集合放到一个按照类型的序数进行索引的数组中实现:

public static void main(String[] args) {
Herb[] garden = {new Herb("a", Herb.Type.ANNUAL), new Herb("b", Herb.Type.BIENNIAL)}; 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]);
}

这种方法可行,但是由于数组与泛型不兼容,需要进行未受检的转换。

2.更好的方法,数组实际上充当从枚举到值的映射,EnumMap提供这样的实现:

public static void main(String[] args) {
Herb[] garden = {new Herb("a", Herb.Type.ANNUAL), new Herb("b", Herb.Type.BIENNIAL)};
Map<Herb.Type, Set<Herb>> herbByType = new EnumMap<Herb.Type, Set<Herb>>(Herb.Type.class);
for(Herb.Type t : Herb.Type.values())
herbByType.put(t, new HashSet<Herb>());
for(Herb h : garden)
herbByType.get(h.type).add(h);
System.out.println(herbByType);
}

不存在不安全的类型转换。

还可能遇到按照序数进行两次索引的数组的数组

public enum Phase {
SOLID, LIQUID, GAS;
public enum Transition {
MELT, FREEZE, BOTL, CONDENSE, SUBLIME, DEPOSIT;
private static final Transition[][] TRANSITIONS = {
{null, MELT, SUBLIME},
{FREEZE, null, BOTL},
{DEPOSIT, CONDENSE, null}
};
public static Transition from(Phase src, Phase dst) {
return TRANSITIONS[src.ordinal()][dst.ordinal()];
}
}
}

利用EnumMap可以做的更好:

public enum Phase {
SOLID, LIQUID, GAS; public enum Transition {
MELT(SOLID,LIQUID), FREEZE(LIQUID, SOLID),
BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID),
SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID); private final Phase src;
private final Phase dst; Transition(Phase src, Phase dst) {
this.src = src;
this.dst = dst;
} private static final Map<Phase, Map<Phase, Transition>> m =
new EnumMap<Phase, Map<Phase, Transition>>(Phase.class);
static {
for(Phase p : Phase.values())
m.put(p, new EnumMap<Phase, Transition>(Phase.class));
for(Transition t : Transition.values())
m.get(t.src).put(t.dst, t);
} public static Transition from(Phase src, Phase dst) {
return m.get(src).get(dst);
}
}
}

不需要平方级大小的数组。

如果增加一个新的阶段plasma(离子),只有两个过渡与这个阶段关联,电离化,将气体变成离子,消电离化,将离子变成气体。

基于数组的程序,必须给Phase添加一个PLASMA常量,给Phase.Transition添加两种常量,用新的16个元素的版本取代原来9个元素的数组的数组,这很容易出错。

基于EnumMap的版本,所要做的只是将PLASMA添加到Phase列表,并把IONIZE(GAS, PLASMA), DEIONIZE(PLASMA, GAS) 添加到Phase.Transition列表中,出错的概率很小。

第33条:用EnumMap代替序数索引的更多相关文章

  1. Effective Java 第三版——37. 使用EnumMap替代序数索引

    Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...

  2. 用EnumMap代替序数索引

    用EnumMap代替序数索引   有时候,会见到利用ordinal方法来索引数组的代码.例如下面这个简化的类,表示一种烹饪用的香草: public class Herb { public enum T ...

  3. 【Effective Java】9、使用EnumMap代替序数索引

    package cn.xf.cp.ch02.item33; import java.util.EnumMap; import java.util.HashSet; import java.util.M ...

  4. 使用 ENUMMAP 替代序数索引

    import java.util.Arrays; import java.util.EnumMap; import java.util.HashSet; import java.util.Map; i ...

  5. 33条C#、.Net经典面试题目及答案

    33条C#..Net经典面试题目及答案[zt] 本文集中了多条常见的C#..Net经典面试题目例如".NET中类和结构的区别"."ASP.NET页面之间传递值的几种方式? ...

  6. 33条C#、.Net经典面试题目及答案[zt]

    33条C#..Net经典面试题目及答案[zt] 本文集中了多条常见的C#..Net经典面试题目例如“.NET中类和结构的区别”.“ASP.NET页面之间传递值的几种方式?”,并简明扼要的给出了答案,希 ...

  7. 特级教师总结的教育之33条(ZZ)

    一位班主任发给家长的一则短信:“无论成绩好坏,请想想:每个孩子都是种子,只不过每个人的花期不同.有的花,一开始就灿烂绽放:有的花,需要漫长的等待.不要看着别人怒放了,自己的那棵还没有动静就着急,相信是 ...

  8. 33条C#和.NET经典面试题目及答案

    1. .NET中类和结构的区别? 答:结构和类具有大体的语法,但是结构受到的限制比类要多. a. 结构不能有默认的构造函数,因为结构的副本是用编译器创建和销毁的,所以不需要默认的构造函数和析构函数. ...

  9. mysql索引合并:一条sql可以使用多个索引

    前言 mysql的索引合并并不是什么新特性.早在mysql5.0版本就已经实现.之所以还写这篇博文,是因为好多人还一直保留着一条sql语句只能使用一个索引的错误观念.本文会通过一些示例来说明如何使用索 ...

随机推荐

  1. 如何使用validate.js进行动态添加和移除表单验证信息

    表单是我们在开当中的常客,那么对表单的验证也是必须的,那么如何实现动态给表单添加验证规则呢? 方法: 1,动态添加验证规则 // 添加$("#addConnectUser").ru ...

  2. Myeclipse:No projects are available for deployment to this server!

    这是因为以前的项目不是用myEclipse创建的,所以用myeclipse deploy的时候找不到你的项目. 可以这样做:右击原项目名 - myeclipse - Add myeclipse  We ...

  3. 理解 traits

    1.为什么使用traits? 考虑下面的需求,实现一个方法Advance(iter,n),接收一个迭代器iter和移动距离n,将iter向前移动n个距离. 分析,因为存在不同类型的迭代器,做同一件事情 ...

  4. Mysql 5.5 replication 多数据库主从备份Master-Slave配置总结

    配置Mysql server 5.5 的双机备份,也就是master-slave模式.本例子还是一个多database复制的情况. 现在有两个database在同一台mysql server,也就是m ...

  5. 2、netlink简介

    Netlink 是一种特殊的 socket,它是 Linux 所特有的,类似于 BSD 中的AF_ROUTE 但又远比它的功能强大,目前在最新的 Linux 内核(2.6.14)中使用netlink ...

  6. h5拖放-基础知识

    <!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...

  7. git 配置用户名和邮箱

    在安装了git for windows之后,个人总是忘记配置git config的命令,以此记录一下: 配置用户名和邮箱的命令 git config --global user.name " ...

  8. (转)战斗bug技巧全攻略

    原文地址:http://www.cnblogs.com/manuosex/p/3736077.html 程序员不是有一幅这样的对联吗 上联:一个项目两部电脑三餐盒饭只为四千工资搞得五脏俱损六神无主仍然 ...

  9. dirname(__FILE__) 的使用总结

    dirname(__FILE__) php中定义了一个很有用的常数,即 __file__ 这个内定常数是当前php程序的就是完整路径(路径+文件名). 即使这个文件被其他文件引用(include或re ...

  10. mvc模式实现

    listdemo.html负责显示,listModel.class.php负责从数据库存储数据和查找数据,mysql.class.php是操作数据库的类,但不直接使用,model类调用mysql,li ...