用EnumSet代替位域。

来源:互联网 发布:致真大厦 中电数据服务 编辑:程序博客网 时间:2024/05/01 21:56

如果一个枚举类型的元素主要用在集合中,一般就使用int枚举模式,将2的不同倍数赋予每个常量:

// Bit field enumeration constants - OBSOLETE!

public class Test {

public static final int STYLE_BOLD= 1 << 0; // 1

public static final int STYLE_ITALIC = 1 << 1; // 2

public static final int STYLE_UNDERLINE= 1 << 2; // 4

public static final int STYLE_STRIKETHROUGH  = 1 << 3; // 8

// Parameter is bitwise OR of zero or more SYTLE_ constants

public void applyStyles(int styles) { ... }

}

这种表示法让你用OR位运算将几个常量合并到一个集合中,称作位域(bit field)。

text.applyStyles(STYLE_BOLD | STYLE_ITALIC);

位域表示法也允许利用位操作,有效地执行像union(联合)和intersection(交集)这样的集合操作。但位域有着int枚举常量的所有缺点,甚至更多。当位域以数字形式打印时,翻译位域比翻译简单的int枚举常量要困难得多。甚至,要遍历位域表示的所有元素也没有很容易的方法。

有些程序员优先使用枚举而非int常量,他们在需要传递多组常量集时,仍然倾向于使用位域。其实没有理由这么做,因为还有更好地替代方法。java.util包提供了EnumSet类来有效的表示从单个枚举类型中提取的多个值得多个集合。这个类实现Set接口,提供了丰富的功能、类型安全性,以及可以从任何其他Set实现中得到的互用性。但是在内部具体的实现上,每个EnumSet就是用单个long来表示,因此它的性能比得上位域的性能。批处理,如removeAll何retainAll,都是利用位算法来实现的,就像手工替位域实现的那样。但是可以避免手工位操作时容易出现的错误以及不大雅观的代码,因为EnumSet替你完成了这项艰巨的工作。

下面是前一个范例改成用枚举代替位域后的代码,他更加简短、更加清楚,也更加安全:

// EnumSet - a modern replacement for bit fields

public class Text {

public enum Style {BOLD , ITALIC , UNDERLINE , STRIKETHROUGH}

// Any Set could be passed in , but EnumSet is clearly best

public void applyStyles(Set<Style> styles) { ... }

}

下面是将EnumSet实例传递给applyStyles方法的客户端代码。EnumSet提供了丰富的静态工厂来轻松创建集合,其中一个如这个代码所示:

text.applyStyles(EnumSet.of(Style.BOLD , Style.ITALIC));

注意applyStyles方法采用的是Set<Style>而非EnumSet<Style>,虽然看起来好像所有的客户端都可以将EnumSet传到这个方法,但是最好还是接受接口类型而非接受实现类型。这是考虑到可能会有特殊的客户端要传递一些其他的Set实现,并且没有什么明显的缺点。

总而言之,正是因为枚举类型要用在集合(Set)中,所以没有理由用位域来表示他。EnumSet类集位域的简洁和性能优势及枚举类型的所有优点于一身。实际上EnumSet有个缺点,即截止Java 1.6发行版本,他都无法创建不可变的EnumSet,但是这一点很可能在即将出现的版本中得到修正。同时,可以用Collections.unmodifiableSet将EnumSet封装起来,但是间接性和性能会受到影响。