如果枚举类型的元素主要用于集合,传统上使用int枚举类型( item34)为每个常量分配2的不同幂:
该表示形式允许你使用按位或操作将数个常量组成一个集合,称为位字段:
text.applyStyles(STYLE_BOLD | STYLE_ITALIC);
位字段表示还允许你使用按位算法高效的执行集合操作,如并和交。但是位字段有int常量和更多的缺点。当一个位字段被打印为一个数字时,它比一个简单的int枚举常量更加难解释。没有一种简单的方法来迭代由位字段表示的所有元素。最后,你必须预测在编写API时所需的最大位数,并相应地为位字段选择一个类型(通常位int或long)。一旦你选择了一个类型,如果不更改API,就不能超过它的宽度(32或64位)。
一些优先使用枚举而不是int常量的程序员在需要传递一组常量时仍然坚持使用位字段。没有理由这么做,因为有更好的替代方法存在。java.util包提供了EnumSet类来高效的表示从单个枚举类型中提取的值集。这个类实现了Set接口,提供所有的丰富,类型安全和从其他Set实现中的互操作性。但是在内部,每个EnumSet都表示为位向量。如果基础枚举类型有64个或更少的原色(大多数是这样的),则整个EnumSet由一个single long表示,因此它的性能可与位字段的性能相媲美。扩展操作,比如removeAll,和retainAll,使用按位算数实现,就像手动处理位字段一样。但是你不受手工旋转的丑陋和错误倾向的影响:EnumSet为你做了艰苦的工作。
下面是前面的实例在修改以使用枚举和枚举set而不商位字段的样子。它更短,更清晰,更安全:
下面是将EnumSet实例传递到applyStyles方法的客户端代码。EnumSet类为易于创建集提供了一组丰富的静态工厂,其中之一在下面的代码中得到了说明:
text.applyStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
注意applyStyles方法使用Set<Style>而不是EnumSet<Style>。虽然似乎所有客户端都会将EnumSet传递给该方法,但通常接口接口类型而不是实现类型是很好的做法(item64 )。这允许不寻常的客户端传递其他集合实现的可能性。
总之,仅仅因为枚举类型将在集合中使用,就没有理由用位字段来表示。EnumSet类联合了位字段的简洁与性能以及许多在item34描述的枚举类型的好处。EnumSet的一个真正的缺点是,从Java9开始,创建一个不可变的EnumSet是不可能的,但是在即将发布的版本中可能会对此进行补救。同时,你可以将EnumSet封装为Collections.unmodifiableSet。但是间接性和性能将受到影响。
本文写于2019.7.8,历时1天