一、使用EnumSet替换标志
Set它是一种只能添加不重复对象的集合。当然,enum也要求其成员是唯一的,所以enum也有集合行为。但是,因为不能从enum删除或添加元素,因此只能算是不太有用的集合。java SE5引入EnumSet,是为了通过enum以替代传统为基础,创造替代品int位标志。这种标志可以用来表示某种“开/关”信息,不过,使用这种标志,我们最终操作的只是一些bit,而不是这些bit要表达的概念,很容易写出难以理解的代码。
EnumSet设计充分考虑了速度因素,因为它必须非常高效bit标志竞争(其操作与HashSet相比之下,非常快)。就内部而言,它(可能)是一个long因此,值作为比较向量EnumSet非常快速高效。使用EnumSet其优点是,它具有更好的表达能力,在解释一个二进制位置是否存在时必担心性能。
EnumSet元素必须来自一个元素enum。下面的enum报警传感器的位置表示在建筑物内:
package enumerated; public enum AlarmPoints { STAIR1, STAIR2, LOBBY, OFFICE1, OFFICE2, OFFICE3, OFFICE4, BATHROOM, UTILITY, KITCHEN }
然后,我们用EnumSet跟踪报警器的状态:
package enumerated; import static enumerated.AlarmPoints.*; import java.util.EnumSet; public class EnumSets { public static void main(String[] args) { EnumSet<AlarmPoints> points = EnumSet.noneOf(AlarmPoints.class);// Empty set points.add(BATHROOM); System.out.println(points); points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN)); System.out.println(points); points = EnumSet.allOf(AlarmPoints.class); points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN)); System.out.println(points); points.removeAll(EnumSet.range(OFFICE1, OFFICE4)); System.out.println(points); points = EnumSet.complementOf(points); System.out.println(points); } }
使用static import可以简化enum常用。EnumSet方法的名称相当直观,可以查阅JDK找到文档的完整详细描述。若仔细研究EnumSet你还会发现一个有趣的地方:of()方法多次重载,不仅重载可变数量参数,还重载接收2-5个显式参数。这也是从侧面表现出来的EnumSet关注性能。因为只使用可变参数可以解决整个问题,但对比显式参数会有一点性能损失。使用当前的设计,但您只使用2到5个参数调用of()方法时,可以调用相应的重载方法((稍快一点),当您使用一个或五个以上的参数时,您将调用可变参数of()方法。请注意,如果您只使用一个参数,编译器不会构建可变参数的数组,因此与只调用一个参数的方法相比,不会有额外的性能损失。
EnumSet的基础是long,一个long值64位,而一位enum只有一个例子bit这意味着它是否存在。,不超过一个long在表达能力的情况下,你的EnumSet可以应用于最多不超过64个元素的enum。如果enum超过64个元素会发生什么?
package enumerated; import java.util.EnumSet; public class BigEnumSet { enum Big { A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75 } public static void main(String[] args) { EnumSet<Big> bigEnumSet = EnumSet.allOf(Big.class); System.out.println(bigEnumSet); } }
显然,EnumSet可应用于64多个元素enum,所以我猜,Enum必要时会增加一个long。
二、使用EnumMap
EnumMap是一种特殊的Map,它需要键(key)必须来自一个enum。由于enum所以EnumMap数组可以在内部实现。因此EnumMap速度很快,我们可以放心使用enum实例在EnumMap搜索操作。但是,我们只能做到enum用键调用实例put()其他操作和使用方法Map差不多。
下面的例子展示了命令设计模式的使用。一般来说,命令模式首先需要一个只有单一方法的接口,然后从接口中实现不同行为的多个子类。接下来,程序员可以构建命令对象命令对象并使用它们:
package enumerated; import java.util.EnumMap; import java.util.Map; import static enumerated.AlarmPoints.*; interface Command { void action(); } public class EnumMaps { public static void main(String[] args) { EnumMap<AlarmPoints, Command> em = new EnumMap<>(AlarmPoints.class); em.put(KITCHEN, () -> { System.out.println("Kitchen fire!"); }); em.put(BATHROOM, () -> { System.out.println("Bathroom alert!"); }); for (Map.Entry<AlarmPoints, Command> e : em.entrySet()) { System.out.print(e.getKey() ": "); e.getValue().action(); } // 若某个键没有值 try { em.get(UTILITY).action(); } catch (Exception e1) { e1.printStackTrace(); } } }
与EnumSet一样,enum实例定义中的顺序决定了它EnumMap中的顺序。
main()方法最后部分说明,enum每一个例子作为一个键总是存在的。但是,如果你不调用这个键put()存储相应值的方法,相应值为null。
与常量相关的方法(constant-specific methods与下一节相比,EnumMap有一个优点,那EnumMap允许程序员改变值对象,常量相关方法在编译期固定。
稍后你会看到,在你有多种类型的enum,我们可以使用它们之间的相互操作EnumMap实现多路分发(multiple dispatching)。