为了更好的使用枚举,Java提供了两个枚举集合:EnumSet和EnumMap,这两个集合的使用方法都比较简单,EnumSet表示其元素必须是某一枚举的枚举项,EnumMap表示Key值必须是某一枚举的枚举项,由于枚举类型的实例数量固定并且有限,相对来说,EnumSet和EnumMap的效率会比其他Set和Map要高.

虽然EnumSret很好用,但是它有一个隐藏的特点.项目中可能定义非常多的枚举项,然后通过EnumSet访问,遍历,但它对不同的枚举数量有不同的处理方式.为了进行对比,我们定义两个枚举,一个数量等于64,一个是65(大于64即可,为什么是64而不是128,512呢?)代码如下:

 1 import java.util.EnumSet;
 2
 3
 4 public class Client {
 5     public static void main(String[] args) {
 6         //创建生成包含所有枚举项的EnumSet
 7         EnumSet<Const> cs = EnumSet.allOf(Const.class);
 8         EnumSet<LargeConst> lcs = EnumSet.allOf(LargeConst.class);
 9         //打印出枚举项数量
10         System.out.println("Const枚举项数量:" + cs.size());
11         System.out.println("LargeConst枚举项数量:" + lcs.size());
12         //输出两个EnumSet的class
13         System.out.println(cs.getClass());
14         System.out.println(lcs.getClass());
15     }
16 }
17
18 //普通枚举项,数量小于64
19 enum Const {
20     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;
21 }
22 //大枚举,数量超过64
23 enum LargeConst {
24     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;
25 }

Const中的枚举项数量是64,LargeConst的数量是65,上面的代码让他们转换成EnumSet,然后判断一下它们的class类型是否相同.

运行结果:

Const枚举项数量:64
LargeConst枚举项数量:65
class java.util.RegularEnumSet
class java.util.JumboEnumSet

很遗憾,两者不相等,就差1个元素,两者就不相同了,这也是我们要重点关注枚举项数量的原因,通过源码看Java是如何处理的?首先跟踪allOf方法,其源代码如下:

 1     /**
 2      * Creates an enum set containing all of the elements in the specified
 3      * element type.
 4      *
 5      * @param elementType the class object of the element type for this enum
 6      *     set
 7      * @throws NullPointerException if <tt>elementType</tt> is null
 8      */
 9     public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) {
10         EnumSet<E> result = noneOf(elementType);//生成一个空的EnumSet
11         result.addAll();..加入所有的枚举项
12         return result;
13     }

allOff通过noneOf方法首先生成一个EnumSet对象,然后把所有的枚举项都加进去,问题可能就出现在EnumSet的生成上了.查看noneOf的代码.

 1     /**
 2      * Creates an empty enum set with the specified element type.
 3      *
 4      * @param elementType the class object of the element type for this enum
 5      *     set
 6      * @throws NullPointerException if <tt>elementType</tt> is null
 7      */
 8     public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
 9         Enum[] universe = getUniverse(elementType);
10         if (universe == null)
11             throw new ClassCastException(elementType + " not an enum");
12
13         if (universe.length <= 64) //枚举数量小于64
14             return new RegularEnumSet<>(elementType, universe);
15         else //枚举数量大于64
16             return new JumboEnumSet<>(elementType, universe);
17     }

当枚举数量小于64的时候,创建一个RegularEnumSet实例对象,大于64时则创建一个JumboEnumSet实例对象.

为什么要这么处理?这还要看着两个类之间的差异.

RegularEnumSet类,代码如下:

 1 class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
 2     private long elements = 0L;//记录所有枚举排序号,注意是long型
 3     RegularEnumSet(Class<E>elementType, Enum[] universe) {//构造函数
 4         super(elementType, universe);
 5     }
 6     void addAll() {//加入所有元素
 7         if (universe.length != 0)
 8             elements = -1L >>> -universe.length;
 9     }
10 }    

枚举项的排序值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的时候.简直非一般的速度.

转载于:https://www.cnblogs.com/DreamDrive/p/5639983.html

[改善Java代码]枚举项的数量限制在64个以内相关推荐

  1. 枚举项的数量限制在64个以内

    为了更好的使用枚举,Java提供了两个枚举集合:EnumSet和EnumMap,这两个集合的使用方法都比较简单,EnumSet表示其元素必须是某一枚举的枚举项,EnumMap表示Key值必须是某一枚举 ...

  2. java 代码解析工具_改善 Java 代码质量的工具与方法

    原标题:改善 Java 代码质量的工具与方法 我们可能见过上面的有关代码质量的图片,究竟如何衡量一段代码好坏? 代码质量是什么?为什么它很重要? 作家通过他的著作来讲述了一个清晰的.令人信服的故事.他 ...

  3. 改善Java代码有哪些方法?

    前言 Java是一门优秀的面向对象的编程语言,针对遇到同样的一个问题会有很多中解法哪种实现方法是最好的呢,还需要不断的探究JDK的底层原理.我会例出Java改善的建议哦,希望大家可以在平时开发工作去使 ...

  4. java switch null_[改善Java代码]小心switch带来的空值异常

    使用枚举定义常量时,会伴有大量的switch语句判断,目的是伪类每个枚举项解释其行为,例如: 1 public classClient {2 public static voidmain(String ...

  5. [改善Java代码]使用valueOf前必须进行校验

    每个枚举都是java.lang.Enum的子类,都可以访问Enum类提供的方法,比如hashCode(),name(),valueOf()等..... 其中valueOf()方法会把一个String类 ...

  6. java代码获取项路径_Java中获取各种路径的方法盘点

    Java代码中获取路径 假设当前项目路径为:D:/wunian/workspace/JavaProject,现在来分别获取此项目中的各种路径. 1.获取资源文件(.class文件)所在路径(四种方式) ...

  7. [改善Java代码]减少HashMap中元素的数量

    在系统开发中我们经常会使用HashMap作为数据集容器,或者是用缓冲池来处理,一般很稳定,但偶尔也会出现内存溢出的问题(OutOfMemory错误),而且这经常是与HashMap有关的.而且这经常是与 ...

  8. java 不可修改的集合对象_[改善Java代码]asList方法产生的List对象不可更改

    上一个建议之处了asList方法在转换基本类型数组时候存在的问题,在看下asList方法返回的列表有何特殊的地方.看代码: importjava.util.Arrays;importjava.util ...

  9. [改善Java代码]线程优先级只使用三个等级

    线程的优先级(priority)决定了线程获得CPU运行的机会,优先级越高获得的运行机会越大,优先级越低获得的机会越小.Java的线程有10个级别(准确的说是11个级别,级别为0的线程是JVM,应用程 ...

最新文章

  1. Python 骚操作,微信远程控制电脑
  2. Kettle使用_3变量替换之获取系统信息与命名参数
  3. oracle中简单查询语句的格式及执行顺序分析
  4. Nginx配置相关结构划分的技巧和禁止IP访问
  5. flex 组建重写
  6. 链表逆序的原理及实例
  7. 工作295:发布逻辑处理
  8. PHP7数字三角形代码,倒数字三角
  9. 19.Linux-USB总线驱动分析
  10. spring eurake中使用IP注册
  11. Windows修改hosts
  12. CentOS6编译装载nbd模块
  13. 游戏服务器信息未初始化,游戏服务器初始化
  14. 仓库出租平台有哪些_日用品仓库出租哪家划算周到
  15. Hibernate方法save、update、merge、saveOrUpdate及get和load的区别
  16. ubb php论坛程序,论坛UBB代码 推荐
  17. bat 一键清理系统垃圾的修改
  18. 霍夫斯特拉大学计算机科学专业排名,院校排名
  19. 故事系列之二:佛法世界中看天赋与勤奋
  20. 中移动Open NFV实验室迎新合作伙伴

热门文章

  1. 如何使用VS2017将客户端库添加到ASP.NET CORE 2.2(简单方法)
  2. 计算机应用技术知识,计算机应用技术主要学什么
  3. java 数组map_Java之数组array和集合list、set、map
  4. java 上传图片后没法立马显示出来_SpringMVC多个文件上传及上传后立即显示图片功能...
  5. vue-quill-editor 获取无法获取光标位置
  6. tplink无线受限 服务器无响应,tplink怎么设置密码(tplink服务器无响应)
  7. ps cc 生成html,使用photoshop生成网页的方法
  8. 为什么spyder这么慢_微区成分分析为什么这么慢?
  9. java字符串相关知识
  10. php 图片文件转base64编码格式,php如何将图片转为base64编码格式