有时候,会见到利用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. EF中Database.SqlQuery

    本文转载:http://www.cnblogs.com/daimage/archive/2012/07/04/2575844.html EF中Database.SqlQuery<TElement ...

  2. 【36】绝不重新定义继承而来的non-virtual方法

    1.绝不重新定义继承而来的non-virtual方法,为什么? 首先想想,non-virtual方法是干什么的?也就是说,它的使用场景.父类的non-virtual方法,其实就是告诉子类,继承实现,子 ...

  3. 应用之星推出“图文app”制作工具,并附上教程

    应用之星已推出的"图文"app制作工具,是高速制作图文电子书,图文杂志等一切有关图文资料的app生成工具,以下跟大家介绍"图文"制作教程,简单快捷,大致分三大步 ...

  4. 【转】Oracle 10g RAC TAF

    本人转自:http://www.cnblogs.com/future2012lg/archive/2013/10/12/3365978.html Oracle RAC 同一时候具备HA(High Av ...

  5. 信号之sigsetjmp和siglongjmp函数

    在信号处理程序中经常调用longjmp函数以返回到程序的主循环中,而不是从该处理程序返回. 但是,调用longjmp有一个问题.当捕捉到一个信号时,进入信号捕捉函数,此时当前信号被自动地加到进程的信号 ...

  6. keyStore vs trustStore--转载

    原文:http://lukejin.iteye.com/blog/605634 今天有同事向我问起这两个概念,所以我就记录下.首先我们得澄清一些概念.一个web应用如果需要提供以https的方式访问的 ...

  7. 使用HttpClient发送请求、接收响应

    使用HttpClient发送请求.接收响应很简单,只要如下几步即可. 1.创建HttpClient对象.  CloseableHttpClient httpclient = HttpClients.c ...

  8. lvm基础

    一.简介 LVM是 Logical Volume Manager(逻辑卷管理)的简写. LVM将一个或多个硬盘的分区在逻辑上集合,相当于一个大硬盘来使用,当硬盘的空间不够使用的时候,可以继续将其它的硬 ...

  9. C语言位运算符:与、或、异或、取反,左移和右移

    C语言位运算符:与.或.异或.取反.左移和右移 个位操作运算符.这些运算符只能用于整型操作数,即只能用于带符号或无符号的char,short,int与long类型. ,则该位的结果值为1,否则为0 | ...

  10. 类结构体 与 byte[] 转换类

    public static class StructConvert { public static object BytesToStruct(byte[] bytes, Type strcutType ...