ITEM 36: 使用 ENUMSET 而不是 bit 位域

2019-07-26  本文已影响0人  rabbittttt

ITEM 36: USE ENUMSET INSTEAD OF BIT FIELDS
  如果枚举类型的元素主要集合起来使用,则传统上使用 int enum 模式(item 34),为每个常量分配不同的二进制位置:

/ Bit field enumeration constants - OBSOLETE!
public class Text {
  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 STYLE_ constants
  public void applyStyles(int styles) { ... } 
}

  这种表示方式允许您使用位或操作将几个常量组合成一个集合,称为位字段:
text.applyStyles(STYLE_BOLD | STYLE_ITALIC);
  位字段表示还允许使用位算法高效地执行集合操作,比如合集和交集。但是位域具有int枚举常量等的所有缺点。当一个位字段被打印为一个数字时,它比一个简单的 int enum 常量更难解释。没有简单的方法可以遍历由bit字段表示的所有元素。最后,您必须在编写API时预测所需的最大比特数,并相应地为比特字段选择一种类型(通常是int或long)。一旦选择了一种类型,如果不更改API,就不能超过它的宽度(32位或64位)。
  一些使用枚举而不是 int 常量的程序员在需要传递常量集时仍然坚持使用位字段。没有理由这样做,因为存在更好的选择。java.util 包提供EnumSet 类来有效地表示从单个 enum 类型绘制的值集。这个类实现了 Set 接口,提供了与任何其他 Set 实现相同的所有丰富性、类型安全性和互操作性。但在内部,每个枚举集都表示为一个位向量。如果底层枚举类型有 64 个或更少的元素(大多数元素都有),则整个 EnumSet 用一个 long 表示,因此它的性能可以与 bit 字段的性能相媲美。批量操作(如 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 传递给方法,但通常接受接口类型而不是实现类型(item 64)是一种很好的实践。这允许异常客户端传入其他一些集合实现的可能性。
  总之,仅仅因为在集合中使用枚举类型,就没有理由用位字段表示它。EnumSet 类结合了 bit 字段的简洁性和性能,以及item 34 中描述的枚举类型的所有优点。EnumSet 的一个真正缺点是(Java 9 也存在这个问题),它不可能创建不可变的 EnumSet,但是在即将发布的版本中,这一点可能会得到纠正。同时,您可以用 Collections.unmodifiableSet 包装 EnumSet,但简洁和性能将受到影响。

上一篇下一篇

猜你喜欢

热点阅读