JDK源码解读之RegularEnumSet

Java EnumSet工作原理初窥 EnumSet是Java枚举类型的泛型容器,Java既然有了SortedSet、TreeSet、HashSet等容器,为何还要多一个EnumSet<T>呢?答案肯定是EnumSet有一定的特性,举个例子,EnumSet的速度很快。其他特性不一一列举了,毕竟本文的内容不是介绍EnumSet的特性。   首先以事实说话,存在这样一个EnumSet,它有50个枚举值T0~T49,将50个值插入到容器(HashSet、EnumSet)中,为一个操作,将50个枚举值移出做为第二个操作。把第一个和第二个操作执行的总时间设定为一个周期,拿HashSet操作的一个周期和EnumSet的一个周期做比较自然没什 立即下载

内容主要转载自usafchn's Notes,然后在此基础上做了一些补充。

缘由

今天做项目的时候偶然用到EnumSet,EnumSet平时不太常用,比较陌生,于是点进去看了下源码,发现这个类还是比较有意思的,首先EnumSet是个抽象类,当我们调用EnumSet提供的静态函数创建对象的时候,实际创建的是RegularEnumSet或者JumboEnumSet,前者对应枚举成员少于64个的情况,后者不设枚举成员数量上限,当枚举成员数量大于64时,EnumSet实际创建的对象是JumboEnumSet类型。由于我定义的枚举成员数明显没到64,于是很自然的点进RegularEnumSet继续一探究竟…

addAll()函数

如果你调用EnumSet的静态函数allOf()函数,那么实际将会调用到的是RegularEnumSet中的addAll()函数,addAll()函数的实现只有一行:

1
2
if (universe.length != 0)
    elements = -1L >>> -universe.length;

真正引起我兴趣的也正是这行代码,先解释一下几个变量的含义:elements是long类型的64为整数,用来存储枚举值;universe是一个数组,里面存放了全部枚举类型,length是数组长度,一个正数,前面加了负号,表示要移的位数是小于0的数。

移位运算

众所周知,Java的移位运算符有三个:<<、>>和>>>,第一个是左移,后两个分别是带符号右移和无符号右移,那么移位运算符的右边竟然是一个负数,到底什么意思呢?百度一下无果,于是想到了Oracle官方的JAVA语言规范[^java],翻了一下,好家伙,官方文档果然对移位运算规定的清清楚楚,其描述是这样的:

If the promoted type of the left-hand operand is int, only the five lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & with the mask value 0x1f (0b11111). The shift distance actually used is therefore always in the range 0 to 31, inclusive.

If the promoted type of the left-hand operand is long, then only the six lowest- order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & with the mask value 0x3f (0b111111). The shift distance actually used is therefore always in the range 0 to 63, inclusive.

看到了吧,大意就是移位操作符左边如果是int类型,则操作符右边的数只有低5位有效(右边的数会首先与0x1f做AND运算),如果操作符左边是long类型,右边的数就只取低6位为有效位。

再看addAll()函数

回顾一下前面提到的表达式:

1
-1L >>> -universe.length;

左边是一个long类型,-1L的补码表示是0xffffffffffffffff,并且>>>是无符号右移,在右移的时候最高位补0;右边的”-universe.length”其实只有低6位有效,举个例子来说吧,假设length为5,那么-5在内存中的表示为0xfffffffb,取低6位有效,那么实际有效值是0x3b,换成十进制就是59,也就是把-1L右移59位,可见,表达式的结果正好是低5位全1,高位全0。

更一般地,当n处在[1..64]之间时,(-1L >>> -n)的结果应该是低n位全1,高位全0。可见,这个结果正好满足RegularEnumSet的需要。

### 联想

知道了这个trick以后,其实它还有更多用途,比如可以这样:

1
2
3
4
5
6
7
8
// 代码节选自java.lang.Long类
public static long rotateLeft(long i, int distance) {
    return (i << distance) | (i >>> -distance);
}

public static long rotateRight(long i, int distance) {
    return (i >>> distance) | (i << -distance);
}

是不是很有意思呢?


添加/删除操作

知道移位操作的含义后,再看RegularEnumSet中其它成员函数就非常简单了(本来这个类就没什么技术含量,不是么?),比较有意思的是这个类判断元素是否添加/删除成功的方法,比如在add()函数中,它是这么实现的:

1
2
3
long oldElements = elements;
elements |= (1L << ((Enum)e).ordinal());
return elements != oldElements;

同样,remove()函数中,它是这么实现的:

1
2
3
long oldElements = elements;
elements &= ~(1L << ((Enum)e).ordinal());
return elements != oldElements;

这个类里面判断元素有没有添加/删除成功,它没有事先去判断对应比特位上的数是0还是1,而是看添加/删除后elements数值有没有变化,这个方法在批量添加/删除的时候特别有用(不用一位一位判断了),以后可以借鉴哈。

size()函数

RegularEnumSet中还有一个比较有意思的成员函数是size()函数,size()函数是求Set中包含几个元素,也就是求长整数elements二进制表示中1个个数。

求一个二进制数中1的个数方法太多,有没有较为高效的方法呢?先来看一下JDK是怎么实现的吧:

1
2
3
4
5
6
7
8
9
public static int bitCount(long i) {
	i = i - ((i >>> 1) & 0x5555555555555555L);
	i = (i & 0x3333333333333333L) + ((i >>> 2) & 0x3333333333333333L);
	i = (i + (i >>> 4)) & 0x0f0f0f0f0f0f0f0fL;
	i = i + (i >>> 8);
	i = i + (i >>> 16);
	i = i + (i >>> 32);
	return (int)i & 0x7f;
}

这个方法技巧性很强,初次看很不容易看懂,基本思想是把二进制中相邻位相加,然后以2位为单位再合并,再4位合并……直到把所有位都合并了。上面的代码确实非常难懂,不过可以换种写法,性能略微低点,但是好理解啊:

1
2
3
4
5
6
7
8
9
public static int bitCount(int n) 
{ 
    n = (n &0x55555555) + ((n >>1) &0x55555555) ; 
    n = (n &0x33333333) + ((n >>2) &0x33333333) ; 
    n = (n &0x0f0f0f0f) + ((n >>4) &0x0f0f0f0f) ; 
    n = (n &0x00ff00ff) + ((n >>8) &0x00ff00ff) ; 
    n = (n &0x0000ffff) + ((n >>16) &0x0000ffff) ; 
    return n ; 
}

是不是清楚了很多呢,本人还是比较喜欢这种写法,代码形式也更加对称,可读性还强。

 详细请参阅:《The Java® Language Specification —— Java SE 8 Edition》

===================================================================================================================

上面这篇文章中介绍的方法不多,重点在于移位操作符,个人感觉理解以为操作符是理解EnumSet的关键。

EnumSetIterator的next()方法

这个方法我觉得也很巧妙(大概也是个人水平问题吧,位操作这种用的相当少。)。

public E next() {
            if (unseen == 0)
                throw new NoSuchElementException();
            lastReturned = unseen & -unseen; //unseen & -unseen返回的是unseen的二进制字符串最右边第一个非0位代表的十进制数,自己写写比较一下结果就出来了。
            unseen -= lastReturned;
            return (E) universe[Long.numberOfTrailingZeros(lastReturned)]; //Long.numberOfTrailingZeros()返回的是lastReturned的二进制字符串的最右边连续多少位为0。如果最右边为二进制位为1则结果为0.</span>

        }


EnumSet在构造的时候如果length < 64则返回的是RegularEnumSet,否则返回的是JumboEnumSet。JumboEnumSet和RegularEnumSet基本一致,只不过在保存值的时候使用的是一个long型数组变量,而RegularEnumSet只用一个long型变量保存,JumboEnumSet在做一些操作的时候需要先定位到是数组中那个元素,然后所要做的操作和RegularEnumSet基本上是一样的。

/**
     * Bit vector representation of this set.  The ith bit of the jth
     * element of this array represents the  presence of universe[64*j +i]
     * in this set.
     */
    private long elements[];


C# NX二次开发:两种过滤器设置方案的实战解析与场景适配 本文深入解析C# NX二次开发中的两种过滤器设置方案:AddFilterHandler回调与MaskTriple属性设置。通过实战案例对比其性能差异与适用场景,帮助开发者根据需求选择最优方案,提升NX插件开发效率与交互体验。特别针对BlockStyler集成场景提供专业建议,是NX二次开发者的实用指南。 阅读详情

相关推荐

Zemax序列模式下激光二极管(LD)光源建模与像散校正实战

本文详细讲解了在Zemax序列模式下精确建立激光二极管(LD)光源模型并校正其像散的核心方法。通过将椭圆光束的发散角转换为渐晕系数,并巧妙利用理想柱面镜模拟像散,实现了对LD光源特性的高精度模拟。该方法特别适用于激光耦合、激光雷达等需要对像散光束进行精密控制的光学系统设计。

weixin_28455801的博客 436

EnumSet源码解析

来源: Java编程的逻辑 EnumSet EnumSet的实现与EnumMap没有任何关系,而是用极为精简和高效的位向量实现的; 位向量是计算机程序中解决问题的一种常用方式 1 实现原理 1.1 内部组成 final Class<E> elementType;//类型信息 final Enum[] universe;//枚举类的所有枚举值 EnumSet自身没有记录元素个数的变量,...

248

scDeepCluster_pytorch代码解读与文章理解

论文:Clustering single-cell RNA-seq data with a。

qq_34179307的博客 914

构建智能世界:从物联网架构到实战落地的系统性工程指南

物联网技术通过连接物理设备与数字世界,实现数据的采集、传输与智能处理,其核心价值在于构建一个可感知、可分析、可控制的智能系统。从技术原理上看,它依赖于分层解耦的架构设计,将复杂的系统划分为感知层、网络层、平台层和应用层,并通过微服务化与API化实现能力的灵活组合与复用。这种架构模式能有效支撑海量设备接入与数据处理,其技术价值体现在提升业务自动化水平、优化资源利用和创造新的服务模式上,广泛应用于智慧城市、工业互联网和企业数字化转型等场景。本文聚焦于如何将这一系统性工程落地,深入探讨了以**中台思维**构建共享

weixin_33736832的博客 492

Cadence16.6安装教程

如需安装所需的软件,请到文章末尾处查看 安装步骤: 安装前先关闭杀毒软件和360卫士,注意安装路径不能有中文,安装包路径也不要有中文。 试装系统:win10 64bit 1.解压安装包(共5个压缩包,每个都要解压) 2.以管理员身份运行安装程序。 3.选择License Manager。 4.点击NEXT。 5.选择I accept,然后点击NEXT。 6.选择License Manage...

谢谢关注“小鱼教你模数电”,各平台同号 8万+

从仿真到实践:两级直接耦合放大电路的静态工作点协同调试指南

本文详细解析了两级直接耦合放大电路的静态工作点协同调试方法,从仿真到实践提供系统指南。重点介绍了Rc2电阻调整技巧、集电极电位测量方法及常见问题排查,帮助工程师解决各级静态工作点相互影响的难题,提升模拟电路设计效率。

weixin_29189373的博客 124

java enumset_Java中的EnumSet – RegularEnumSet与JumboEnumSet

Java中的EnumSet是Set 接口的特殊实现,是在Java 1.5语言中首次引入Enum时 将其 添加回Java 1.5中的。 令人惊讶的是,在Java 1.5发行版(2004年9月30日发行,代号为Tiger)的许多年之后,许多Java程序员仍然不知道所引入的几个好功能。 EnumSet 就是这样的示例之一,与 Enum常量的 其他 Set 实现 相比,它确实非常有用并且提供了更高的性...

698

squidpy 教程 1:在 AnnData 和 Squidpy 中导入空间数据

这些是 10x Genomics Visium 的 Space Ranger 输出提供的 keys,因此是第一个被采用的 keys。在这种情况下,我们假设空间坐标与像素具有相同的比例,因此我们将该值设置为 1。为此,我们将使用模拟数据集,并通过示例展示您应该注意的重要细节,以便利用 AnnData-Scanpy-Squidpy 生态系统的全部功能。并不是绝对必要的,但会节省您大量的输入,因为它是 Squidpy 和 Scanpy 的默认值。正如你所看到的,空间坐标被缩小了,图像被“放大”了。

🧑‍🎓 博士研究生在读 🔬 分享生物信息学、神经生物学方面的知识 💬 合作请在后台留言 2255

关于枚举,enum、Enum、EnumSet、RegularEnumSet、JumboEnumSet

Apache Commons Lang。 在版本3中,enum相关的工具就留下EnumUtils。   首先, 所有enum,都默认实现了抽象类 java.lang.Enum 。所以,所有enum都具备Enum中的方法,而且,还有一个values()方法,虽然找不到,但的确可以使用。 其次, JDK提供了 java.util.EnumSet ,专门用于存储同一个enum类型的常量(需要指出,enu...

weixin_33778544的博客 311

【Halcon 】Halcon 实战:如何为 XLD 模板添加极性信息以提升匹配精度?

本文介绍了Halcon中为XLD轮廓模板添加极性信息的实用技巧。通过先以"ignore_local_polarity"模式创建模板,首次匹配获得位置后,使用set_shape_model_metric运算符结合真实图像为模板注入极性信息(边缘明暗方向)。这种方法弥补了XLD模板初始不支持极性的缺陷,可显著提升后续匹配的精度和抗干扰能力。实验显示,添加极性信息后的模板匹配效果明显优于无极性的匹配结果。该技术特别适用于基于手动绘制或处理后的XLD轮廓创建模板的场景。

songhuangong123的博客 750

HandwrittenMathFormulaRecognition:基于YOLOv8的手写数学公式识别系统

输入:包含手写数学公式的图像(拍摄的板书、作业纸等)输出:对应的数字文本形式的数学公式(如LaTeX格式或文本表达式)标准的手写数学表达式数据集,包含在线轨迹及图片,广泛用于公式识别研究。包含近10万对公式图片及对应的LaTeX标注,适合训练公式识别模型。MathBrush公开手写数学公式图像数据集,含多种书写风格。手写数字与符号数据集(MNIST及其扩展)用于基础数学符号的训练。

YOLO 283

工厂模式-以EnumSet源码为例

java.Util.EnumSetpublic static &lt;E extends Enum&lt;E&gt;&gt; EnumSet&lt;E&gt; allOf(Class&lt;E&gt; elementType) { EnumSet&lt;E&gt; result = noneOf(elementType); result.addAll(); ...

cathy_1211的博客 245

Vite 任意文件读取漏洞复现(CVE-2026-39363)

Vite开发服务器 WebSocket中的fetchModule方法未实施server.fs访问控制,导致攻击者可通过WebSocket连接并读取服务器上的任意文件(CVE-2026-39363)

iSee857的博客 220

EnumSet源码分析

核心:long(long数组) 和 位运算, 其存储结构elements并未直接存枚举本身,而是位标识,枚举存于elementType中的enumConstants中. 1、内部元素为枚举; 2、内部表示为位向量,使用“位标志”的替换形式。(类似0x1000000,按bit存储,用位运算进行相关操作); 3、全部是静态方法s...

weixin_33825683的博客 226

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

枚举集合的EnumSet

在上一篇博客文章中,我们发现了EnumMap用于带有枚举键的映射。 您可能已经观察到,还有一个专门针对枚举优化的Set : EnumSet 。 我们再次定义一个CoffeeType枚举: public enum CoffeeType { ESPRESSO, POUR_OVER, FRENCH_PRESS, LATTE, FLAT_WHITE } 现在,我们可以使用EnumSe...

最佳 Java 编程 412

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

努力的小胖学习记录(九)------EnumSet

什么是EnumSet 从上图可以看出,EnumSet是个抽象类,继承AbstractSet,然后有两个具体子类分别为RegularEnumSet和JumboEnumSet,并且EnumSet只能存放Enum类别的对象。另外我们再看Thinking in java的一句话:EnumSet的设计充分考虑到了速度因素,因为它必须与非常高效的bit标志相竞争。从这句话可以看出,EnumSet是非常之...

小胖的博格 258

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Difference between RegularEnumSet and JumboEnumSet in Java

Difference between RegularEnumSet and JumboEnumSet in Java was asked in a recent Java interview to one of my friend, unfortunately he hasn't explored this topic well and couldn't answer this question

Schen6 Online Life 1187

EnumSet

EnumSet 是一个与枚举类型一起使用的专用 Set 实现。枚举set中所有元素都必须来自单个枚举类型(即必须是同类型,且该类型是Enum的子类)。  枚举类型在创建 set 时显式或隐式地指定。枚举 set 在内部表示为位向量。 此表示形式非常紧凑且高效。此类的空间和时间性能应该很好,  足以用作传统上基于 int 的“位标志”的替换形式,具有高品质、类型安全的优势。  如果指定的 co

Robin Hu的专栏 1万+

Java集合之EnumSet

EnumSetEnumSet 是一个专为枚举设计的集合类,EnumSet中的所有元素都必须是指定枚举类型的枚举值,该枚举类型在创建EnumSet时显式或隐式地指定。 EnumSet的集合元素也是有序的,EnumSet以枚举值在Enum类内的定义顺序来决定集合元素的顺序。 EnumSet在内部以位向量的形式存储,这种存储形式非常紧凑、高效,因此EnumSet对象占用内存很小,而且运行效率很好。尤其是进

桉树先生 8146

Java:关于List类中的add、addAll和set方法

    Java中有一个用于表示线性表的List接口,其中包含add、addAll和set三个用于向表中插入元素的方法,笔者近期在编码过程中,遇到了一些问题,并总结了一些注意事项,mark一下。·add方法:    List接口中的add方法有如下两种重载方式:        ① boolean add(E e);        ② void add(int index, E element);其...

<code> I am a coder </code> 13万+

java enum类型的字段 set_java集合(三)Set集合之EnumSet详解

1.定义:添加枚举类元素的专用集合类2.与其他集合类区别:EnumSet内部实现不使用常见的数据结构,比如数组(ArrayList),链表(LinkedList),哈系表(HashMap、Hashtable、HashSet),红黑树(TreeMap、TreeSet)而是使用位运算完成集合的基本操作EnumSet是抽象类,只能通过静态工厂方法构造EnumSet对象,具体如下:EnumSet none...

weixin_29432775的博客 1093

创建和销毁对象(1)——考虑用静态工厂方法代替构造器

本篇主要讲到创建与销毁对象,何时以及如何创建对象,何时以及避免创建对象,又何时能确保它们能够适时的销毁,以及管理对象销毁之前要进行的一些清理动作。 【1、考虑用静态工厂方法代替构造器。】 优点: (1)静态工厂方式与构造器不同的第一大优势在于,它们有名称。 构造器本身的参数没有描述的很清楚正在返回的对象,有名称的静态工厂会更容易使用。 (2)静态工厂方法与构造器不同的第二大优势在于,不必

yueding_h的博客 610

jdk源码分析--Enum

enum可以定义枚举值,我们通过public enum TestEnum{...} 这样的语句定义一个枚举类型。enum是java的一个保留字,我们似乎没有找到enum的实现代码。不过我们可以通过反编译一个自己定义的枚举类型窥其一斑。 public enum TestEnum { ONE("one", "1"); private String code; private St

hippowc的博客 698

jdk源码——Enum

      这一篇主要介绍一下枚举类的源码,我们应该都是用过枚举类型吧,用法我就不再多说了,如果没有用过,可以在网上搜索一下,用法也是比较简单的。     一,先看一下Enum类的定义吧。public abstract class Enum&lt;E extends Enum&lt;E&gt;&gt; implements Comparable&lt;E&gt;, Serializ...

博客:想飞的猪 1336

JDK源码阅读(五):Enum类

文章目录枚举类型定义一个枚举类型编译反编译总结 枚举类型 定义一个枚举类型 public enum Calculator { ADD,SUBSTRACT,MUTIPLE,DIVIDE; } enum很像特殊的class,实际上enum声明定义的类型就是一个类。 而这些类都是类库中Enum类的子类(java.lang.Enum)。它们继承了这个Enum中的许多有用的方法。 编译...

遇卿 395

Java中的EnumSet_Effective Java 2.0_Item 1知识点

EnumSet是Java Set接口的一个特别实现,在JDK 1.5中开始支持,Enum类型也正式引入到了Java中。与其它保存枚举常量的Set相比,EnumSet具有更好的性能,同时其也是Java中的优秀特性之一。下面从三个方面来介绍EnumSet,what,how,when。

SnailTyan 1371
上一篇: Java NIO系列教程
下一篇: Java 8:HashMap的性能提升
java_4_ever
博客等级 码龄16年 8粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值