[改善Java代码]枚举项的数量限制在64个以内
为了更好的使用枚举,Java提供了两个枚举集合:EnumSet和EnumMap,这两个集合的使用方法都比较简单,EnumSet表示其元素必须是某一枚举的枚举项,EnumMap表示Key值必须是某一枚举的枚举项,由于枚举类型的实例数量固定并且有限,相对来说,EnumSet和EnumMap的效率会比其他Set和Map要高.
虽然EnumSret很好用,但是它有一个隐藏的特点.项目中可能定义非常多的枚举项,然后通过EnumSet访问,遍历,但它对不同的枚举数量有不同的处理方式.为了进行对比,我们定义两个枚举,一个数量等于64,一个是65(大于64即可,为什么是64而不是128,512呢?)代码如下:
import java.util.EnumSet;
public class Client {
public static void main(String[] args) {
//创建生成包含所有枚举项的EnumSet
EnumSet<Const> cs = EnumSet.allOf(Const.class);
EnumSet<LargeConst> lcs = EnumSet.allOf(LargeConst.class);
//打印出枚举项数量
System.out.println("Const枚举项数量:" + cs.size());
System.out.println("LargeConst枚举项数量:" + lcs.size());
//输出两个EnumSet的class
System.out.println(cs.getClass());
System.out.println(lcs.getClass());
}
}
//普通枚举项,数量小于64
enum Const {
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, BA, CA, DA, EA, FA, GA, HA, NA, OA, PA, QA, RA, SA, TA, UA, VA, WA, XA, YA, ZA, BC, CC, DC, EC, FC, GC, HC, IC, JC, KC, LC, MC, NC, OC, PC, QC, RC;
}
//大枚举,数量超过64
enum LargeConst {
A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, AA, BA, CA, DA, EA, FA, GA, HA, IA, JA, KA, LA, MA, NA, OA, PA, QA, RA, SA, TA, UA, VA, WA, XA, YA, ZA, AB, BB, CB, DB, EB, FB, GB, HB, IB, JB, KB, LB, MB;
}
Const中的枚举项数量是64,LargeConst的数量是65,上面的代码让他们转换成EnumSet,然后判断一下它们的class类型是否相同.
运行结果:
Const枚举项数量:64
LargeConst枚举项数量:65
class java.util.RegularEnumSet
class java.util.JumboEnumSet
很遗憾,两者不相等,就差1个元素,两者就不相同了,这也是我们要重点关注枚举项数量的原因,通过源码看Java是如何处理的?首先跟踪allOf方法,其源代码如下:
/**
* Creates an enum set containing all of the elements in the specified
* element type.
*
* @param elementType the class object of the element type for this enum
* set
* @throws NullPointerException if <tt>elementType</tt> is null
*/
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) {
EnumSet<E> result = noneOf(elementType);//生成一个空的EnumSet
result.addAll();..加入所有的枚举项
return result;
}
allOff通过noneOf方法首先生成一个EnumSet对象,然后把所有的枚举项都加进去,问题可能就出现在EnumSet的生成上了.查看noneOf的代码.
/**
* Creates an empty enum set with the specified element type.
*
* @param elementType the class object of the element type for this enum
* set
* @throws NullPointerException if <tt>elementType</tt> is null
*/
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
Enum[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum"); if (universe.length <= 64) //枚举数量小于64
return new RegularEnumSet<>(elementType, universe);
else //枚举数量大于64
return new JumboEnumSet<>(elementType, universe);
}
当枚举数量小于64的时候,创建一个RegularEnumSet实例对象,大于64时则创建一个JumboEnumSet实例对象.
为什么要这么处理?这还要看着两个类之间的差异.
RegularEnumSet类,代码如下:
class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
private long elements = 0L;//记录所有枚举排序号,注意是long型
RegularEnumSet(Class<E>elementType, Enum[] universe) {//构造函数
super(elementType, universe);
}
void addAll() {//加入所有元素
if (universe.length != 0)
elements = -1L >>> -universe.length;
}
}
枚举项的排序值ordinal是从0,1,2,......依次递增的,没有重号,没有跳号,RegularEnumSet就是利用这一点把每个枚举项的ordinal映射到一个long类型的每个位上的,
注意看addAll方法的elements元素,它使用了无符号右移操作,符号位为0,并补充地位,简单的说,Java把一个不多于64个枚举项的枚举映射到了一个long类型变量上,这才是EnumSet处理的重点,其他的size方法,constains方法都是根据elements计算出来的,
一个long类型的数字包含了所有的枚举项,其效率和性能肯定都是非常优秀的.
long类型是64位的,所以RegularEnumSet类型也就只能负责枚举项数量,不大于64的枚举,大于64则由JumboEnumSet处理,看其源代码:
class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
private long elements[];//映射所有的枚举项
JumboEnumSet(Class<E>elementType, Enum[] universe) {//构造函数
super(elementType, universe);
elements = new long[(universe.length + 63) >>> 6];//默认长度是枚举项数量除以64再加1
}
void addAll() {//elements中每个元素表示64个枚举项
for (int i = 0; i < elements.length; i++)
elements[i] = -1;
elements[elements.length - 1] >>>= -universe.length;
size = universe.length;
}
}
JumboEnumSet类把枚举项按照64个元素一组拆分了多组,每组都映射到一个long类型的数字上,然后该数组再放置到elements数组中,简单来说JumboEnumSet类的原理与RegularEnumset相似,只是JumboEnumSet使用了long数组能容纳更多的枚举项.
在我们的开发中很少用到位操作.RegularEnumSet是把每个枚举项编码映射到了一个long类型数字的每个位上.JumboEnumSet是先按照64个一组进行拆分,然后每个组再映射到一个long类型数字的每个位上.从这里可以看出数字编码的奥秘.
EnumSet提供的两个实现都是基本的数字类型操作,其性能肯定比其他的Set类型要好很多,特别是Enum的数量少于64的时候.简直非一般的速度.
[改善Java代码]枚举项的数量限制在64个以内的更多相关文章
- [改善Java代码] 枚举项数量限定为64个以内
建议89:枚举项的数量限制在64个以内 为了更好的使用枚举,java 提供了两个枚举集合:EnumSet和EnumMap,这两个集合的使用都比较简单,EnumSet表示其元素必须是某一枚举的枚举项,E ...
- [改善Java代码]枚举和注解结合使用威力更大
注解的写法和接口很类似,都采用了关键字interface,而且都不能有实现代码,常量定义默认都是pulbic static final类型的. 他们的主要不同点是:注解在interface前加上@字符 ...
- [改善Java代码]使用构造函数协助描述枚举项
一.分析 一般来说,我们经常使用的枚举项只有一个属性,即排序号,其默认值是从0.1.2... ....但是除了排序号外,枚举还有一个(或多个)属性:枚举描述,它的含义是通过枚举的构造函数,声明每个枚举 ...
- [改善Java代码]用枚举实现工厂方法模式更简洁
工厂方法模式(Factory Method Patter)是"创建对象的接口",让子类决定实例化哪一个类,并使一个类的实例化延迟到其子类.工厂方法模式在我们的开发工作中,经常会用到 ...
- [改善Java代码]推荐使用枚举定义常量
枚举和注解都是在Java1.5中引入的,虽然他们是后起之秀,但是功能不容小觑,枚举改变了常量的声明方式,注解耦合了数据和代码. 建议83:推荐使用枚举定义常量 一.分析 常量的声明是每一个项目中不可或 ...
- [改善Java代码]使用valueOf前必须进行校验
每个枚举都是java.lang.Enum的子类,都可以访问Enum类提供的方法,比如hashCode(),name(),valueOf()等..... 其中valueOf()方法会把一个String类 ...
- [改善Java代码]小心switch带来的空值异常
使用枚举定义常量时,会伴有大量的switch语句判断,目的是伪类每个枚举项解释其行为,例如: public class Client { public static void main(String[ ...
- [改善Java代码]异常只为异常服务
异常原本是正常逻辑的补充,但是有时候会被当做主逻辑使用.看如下代码: public class Client { enum Color { Red, Blue; } public static voi ...
- [改善Java代码]在switch的default代码块中增加AssertionError错误
switch的后跟枚举类型,case后列出所有的枚举项,这是一个使用枚举的主流写法,那留着default语句似乎没有任何作用了,程序已经列举出了所有的可能选项,肯定不会执行到default语句,. 错 ...
随机推荐
- homework6-更加简单的题目
又把时间搞错了 以为这次要写客户端程序的博客 没想到这次是“怎么吃” 言归正传 cnblog上面有很多技术博客 http://perhaps.cnblogs.com/archive/2005/08/0 ...
- Delphi实例-IdTCPServer和IdTCPClient的使用(支持文件发送)
相关资料: http://blog.csdn.net/earbao/article/details/46514313 结果注意: 1.Use IdContext.IdGlobal 这两个单元2.不能使 ...
- JSF 2.0 hello world example
In this tutorial, we will show you how to develop a JavaServer Faces (JSF) 2.0 hello world example, ...
- FIREDAC直连ORACLE数据库
UniDac对Oracle的Direct连接,不需要套Oracle客户端dll,deploy时真的时 方便又快捷.FireDac连接Oracle,在没有Oracle Client的情况下,是可以连接上 ...
- Quality Center 使用IE8异常浏览器打开
需要装2个软件 1. Microsoft Visual C++ 2005 Redistributable Package (x64) 2. dotnetfx35.exe 配置IE的选项 使用兼容模 ...
- 提高Scrum站会效率的一个小工具
博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:提高Scrum站会效率的一个小工具.
- struts validate
1 login.jsp方式1 <%@ page language="java" import="java.util.*" pageEncoding=&q ...
- 一,彻底理解第一个C语言程序 Hello World
对于初学者来说,第一个程序一般都是hello world,而且是照着书上一点一点敲的.所以,在初学者眼中,敲出来的第一个程序代码不过是一堆看不懂的英语.而事实上,C语言作为一门语言,是有语法的.所以这 ...
- IPv6 sokcet 编程
IPv6的数据包包头与IPv4的数据包头不一样,所以在IPv6下的socket编程用到的某些结构体和地址转换函数也与IPv4下的socket编程不一样.涉及的结构体有:IPv4中使用sockaddr/ ...
- Git Cmd
http://my.oschina.net/sunboy2050/blog/55749