用EnumSet代替位域

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

// Bit field enumeration constants - OBSOLETE
public class Test{
public static final int STYLE_BOLD = 1<<0;//1
public static final int STYLE_INALIC = 1<<2;//2
public static final int STYLE_UNDERLINE = 1<<3;//4
public static final int STYLE_STRIKETHROUGH = 1<<4;//8
//Parameter is bitwise OR of zero or more STYLE_ constants
public void applyStyles(int styles){...}
}

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

test.applyStyles(STYLE_BOLD | STYLE_INALIC);

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

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

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

// EnumSet -a modern replacement for bit fields
public class Test{
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提供了丰富的静态工厂来创建集合,其中一个如这个代码所示:

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

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

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

ps:官方java 1.8API解释

Like most collection implementations, EnumSet is not synchronized. If multiple threads access an enum set concurrently, and at least one of the threads modifies the set, it should be synchronized externally. This is typically accomplished by synchronizing on some object that naturally encapsulates the enum set. If no such object exists, the set should be "wrapped" using the Collections.synchronizedSet(java.util.Set) method. This is best done at creation time, to prevent accidental unsynchronized access:

 Set<MyEnum> s = Collections.synchronizedSet(EnumSet.noneOf(MyEnum.class));

用EnumSet代替位域的更多相关文章

  1. effective java——32用EnumSet代替位域

    什么是位域?为什么用到它?先来看一个例子: public class Test { public static final byte STYLE_BOLD = 1<<0; // 1 pub ...

  2. 第32条:用EnumSet代替位域

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

  3. 位域(bit fields)简介

    使用位域或位操作移动一个字节中的位 Java中EnumSet代替位域代码详解 关于位域的一些东西 深入理解Java枚举类型(enum) 位域是指信息在存储时,并不需要占用一个完整的字节, 而只需占几个 ...

  4. effective java 学习心得

    目的 记录一下最主要学习心得,不然凭我这种辣鸡记忆力分分钟就忘记白看了... 用静态工厂方法代替构造器的最主要好处 1.不必每次都创建新的对象 Boolean.valueOf Long.valueOf ...

  5. Effective java笔记(五),枚举和注解

    30.用enum代替int常量 枚举类型是指由一组固定的常量组成合法值的类型.在java没有引入枚举类型前,表示枚举类型的常用方法是声明一组不同的int常量,每个类型成员一个常量,这种方法称作int枚 ...

  6. 【Java基础】枚举和注解

    在Java1.5版本中,引入了两个类型:枚举类型enum type和注解类型annotation type. Num1:用enum代替int常量 枚举类型enum type是指由一组固定的常量组成合法 ...

  7. Effective Java 读书笔记之五 枚举和注解

    Java1.5中引入了两个新的应用类型家族,新的类为枚举类型,新的接口为注解类型. 一.用enum代替int常量 1.枚举值由一组固定的常量组成合法值的类型. 二.用实例域代替序数 1.不要根据枚举的 ...

  8. Effective Java 阅读笔记——枚举和注解

    30:用enum代替int常量 当需要一组固定常量的时候,应该使用enum代替int常量,除了对于手机登资源有限的设备应该酌情考虑enum的性能弱势之外. 31:用实例域代替序数 应该给enum添加i ...

  9. [Effective Java]第六章 枚举和注解

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

随机推荐

  1. [Phoenix] 八、动态列

    摘要: 传统关系型数据库的动态列实现只能依赖逻辑层的设计实现,而Phoenix是HBase上的SQL层,借助HBase特性实现的动态列功能,具有高度的灵活性,告别业务逻辑层的复杂设计. 一.概要 动态 ...

  2. Aspose.cells 读取Excel表中的图片问题

    一.说明 本文主要是讲解,怎么使用aspose.cells读取Excel表中的图片,并把图片转换成流或是image对象. 二.开发环境说明 开发工具vs2012,c#语言, 三.Aspose.cell ...

  3. windows 复制 文本文件内容 到剪切板

    shell  打开 type filename | clip

  4. JavaWeb浏览器传值乱码

    在Java服务接口调用中,可能会出现乱码,如下图所示: 这是由编码不一致而致的乱码,可转,如下: name = new String(name.getBytes("ISO-8859-1&qu ...

  5. 诡异的json包含bom头

    今日项目碰到 需要调用php的一个接口  结果一直报返回的json字符串转对象  bom头报错 Exception in thread "main" com.fasterxml.j ...

  6. python:将字典转化为数据框

    my_dict = {,,} import pandas as pd pd.Series(my_dict) fuck i you dtype: int64 一个key只有一个value的字典如果直接转 ...

  7. scanf()函数

    Scanf函数攻略: (A)                格式化说明符 格式字符           说明 %d                 读入十进制整数 %u                 ...

  8. CentOS环境 升级Python2.6.6至2.7.5

      1.查看当前Python版本 # python -V Python 2.6.6 # python -V Python 2.6.6 2.下载Python2.7.5源码 # wget http://p ...

  9. 【servlet学习1】使用eclipse+tomcat开发servlet示例

    零.开发环境 1.eclipse+tomcat(tomcat插件已安装到eclipse中). 一.开发servlet步骤 1.在eclipse中新建工程 File —> New —> Ot ...

  10. JNI——C调用JAVA

    步骤: 1. 创建虚拟机 2. 获得class 3. 实例化对象:获得构造方法(方法名为“<init>”),构造参数,调用方法 4. 调用方法:又分为获得方法,构造方法,调用方法 操作方法 ...