当前位置: 首页 > news >正文

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

为了更好的使用枚举,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的时候.简直非一般的速度.


本文转自SummerChill博客园博客,原文链接:http://www.cnblogs.com/DreamDrive/p/5639983.html,如需转载请自行联系原作者

相关文章:

  • 关于C和C++语言声明和定义的说明
  • Tomcat 8安装与配置
  • [CTO札记]如何测试用户接受度?
  • 启动流程
  • Linux定时执行指定的脚本文件
  • 基于DotNet构件技术的企业级敏捷软件开发平台 - AgileEAS.NET - 会话
  • LINUX DNS服务的配置(二)
  • WCF使用NetTcp传输文件
  • 《WCF技术内幕》翻译4:第1部分_第1章_蓝月亮:商业示例
  • 一个引号导致1个小时网站打不开
  • MySQL令人头疼的Aborted告警案例分析
  • 用C#完成Swift远程推送通知
  • 3不原则:如何在HIT行业找到合适的“东家”
  • 容易被忽视的Linux磁盘配额设置
  • 存储过程与函数的区别
  • 4. 路由到控制器 - Laravel从零开始教程
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • ES6系统学习----从Apollo Client看解构赋值
  • JavaScript 奇技淫巧
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • PermissionScope Swift4 兼容问题
  • Python_网络编程
  • React中的“虫洞”——Context
  • storm drpc实例
  • Webpack4 学习笔记 - 01:webpack的安装和简单配置
  • 复杂数据处理
  • 欢迎参加第二届中国游戏开发者大会
  • 前端_面试
  • 前端技术周刊 2018-12-10:前端自动化测试
  • 微信小程序填坑清单
  • 小程序、APP Store 需要的 SSL 证书是个什么东西?
  • ​3ds Max插件CG MAGIC图形板块为您提升线条效率!
  • #QT(智能家居界面-界面切换)
  • (1)(1.13) SiK无线电高级配置(五)
  • (第二周)效能测试
  • (附源码)springboot 房产中介系统 毕业设计 312341
  • (简单有案例)前端实现主题切换、动态换肤的两种简单方式
  • (原創) 如何優化ThinkPad X61開機速度? (NB) (ThinkPad) (X61) (OS) (Windows)
  • (转贴)用VML开发工作流设计器 UCML.NET工作流管理系统
  • .bat批处理(一):@echo off
  • .NET Framework杂记
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .sh文件怎么运行_创建优化的Go镜像文件以及踩过的坑
  • @RunWith注解作用
  • [ 环境搭建篇 ] 安装 java 环境并配置环境变量(附 JDK1.8 安装包)
  • []sim300 GPRS数据收发程序
  • [20150904]exp slow.txt
  • [autojs]autojs开关按钮的简单使用
  • [BZOJ]4817: [Sdoi2017]树点涂色
  • [C/C++]数据结构 深入挖掘环形链表问题
  • [CentOs7]图形界面
  • [Django开源学习 1]django-vue-admin
  • [EULAR文摘] 脊柱放射学持续进展是否显著影响关节功能
  • [IE编程] 打开/关闭IE8的光标浏览模式(Caret Browsing)
  • [JavaEE]线程的状态与安全