Java 集合:EnumSet 类

EnumSet 是 Java 集合框架中专为枚举类型设计的 Set 实现,具有极高的性能和紧凑的内存占用。它的元素必须是同一枚举类型的实例,内部通过位向量实现,操作效率极高(如 add、remove、contains 等方法几乎是常量时间)。EnumSet 不允许包含 null 元素,且是有序的,元素顺序与枚举类中声明的顺序一致。它没有公共构造方法,通常通过静态方法(如 allOf()、of()、range() 等)创建实例。

  

EnumSet 实现类

EnumSet 是抽象类,其实际实现有两个,如下图:

Java 集合:EnumSet 类

其中:

  • RegularEnumSet  用于枚举常量数量较少(≤64)的情况,使用单个 long 存储

  • JumboEnumSet  用于枚举常量数量较多(>64)的情况,使用 long 数组存储

注意,这两个实现类会根据枚举类型的大小自动选择,无需手动指定。

  

EnumSet 常用方法

  • allOf(Class<E> elementType)  创建包含指定枚举类型所有元素的 EnumSet

  • noneOf(Class<E> elementType)  创建指定枚举类型的空 EnumSet

  • of(E e)  创建包含指定单个枚举元素的 EnumSet

  • of(E e1, E e2)  创建包含指定两个枚举元素的 EnumSet(可支持最多 5 个参数)

  • range(E from, E to)  创建包含从 from 到 to 之间所有枚举元素的 EnumSet

  • copyOf(Collection<E> c)  复制集合 c 中的元素创建 EnumSet

  • add(E e)  添加元素

  • addAll(Collection<? extends E> c)  添加集合 c 中的所有元素

  • remove(E e)  移除元素

  • contains(E e)  判断是否包含元素

  • clear()  清空集合

  • retainAll(Collection<?> c)  仅保留与集合 c 共有的元素

  

EnumSet 简单示例

下面通过一个简单例子介绍 EnumSet 的用法:

package com.hxstrive.java_collection.enumSet;

import java.util.EnumSet;

public class EnumSetDemo {
    private static enum Day {
        MONDAY("星期一", 1),
        TUESDAY("星期二", 2),
        WEDNESDAY("星期三", 3),
        THURSDAY("星期四", 4),
        FRIDAY("星期五", 5),
        SATURDAY("星期六", 6),
        SUNDAY("星期日", 7);

        // 中文名称
        private final String chineseName;
        // 一周中的顺序(1表示周一,7表示周日)
        private final int order;

        /**
         * 构造方法
         * @param chineseName 中文名称
         * @param order 顺序
         */
        Day(String chineseName, int order) {
            this.chineseName = chineseName;
            this.order = order;
        }

        public String getChineseName() {
            return chineseName;
        }

        public int getOrder() {
            return order;
        }

        /**
         * 判断是否为工作日(周一至周五)
         * @return 如果是工作日返回true,否则返回false
         */
        public boolean isWeekday() {
            return this != SATURDAY && this != SUNDAY;
        }
    }

    public static void main(String[] args) {
        // 1. 创建包含所有星期的EnumSet
        EnumSet<Day> allDays = EnumSet.allOf(Day.class);
        System.out.println("所有星期: " + allDays);
        //所有星期: [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY]

        // 2. 创建空的EnumSet
        EnumSet<Day> emptyDays = EnumSet.noneOf(Day.class);
        System.out.println("空集合: " + emptyDays);
        //空集合: []

        // 3. 创建包含指定单个元素的EnumSet
        EnumSet<Day> singleDay = EnumSet.of(Day.FRIDAY);
        System.out.println("只包含周五: " + singleDay);
        //只包含周五: [FRIDAY]

        // 4. 创建包含多个指定元素的EnumSet
        EnumSet<Day> someDays = EnumSet.of(Day.MONDAY, Day.WEDNESDAY, Day.FRIDAY);
        System.out.println("包含周一、周三、周五: " + someDays);
        //包含周一、周三、周五: [MONDAY, WEDNESDAY, FRIDAY]

        // 5. 创建包含一定范围元素的EnumSet(从周一到周五)
        EnumSet<Day> workDays = EnumSet.range(Day.MONDAY, Day.FRIDAY);
        System.out.println("工作日(周一到周五): " + workDays);
        //工作日(周一到周五): [MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY]

        // 6. 创建包含周末的EnumSet(通过复制已有集合的补集)
        EnumSet<Day> weekend = EnumSet.complementOf(workDays);
        System.out.println("周末(周六和周日): " + weekend);
        //周末(周六和周日): [SATURDAY, SUNDAY]

        // 7. 集合操作:添加元素
        EnumSet<Day> holidays = EnumSet.copyOf(weekend);
        holidays.add(Day.FRIDAY); // 假设周五也放假
        System.out.println("假期(周末+周五): " + holidays);
        //假期(周末+周五): [FRIDAY, SATURDAY, SUNDAY]

        // 8. 集合操作:移除元素
        holidays.remove(Day.FRIDAY);
        System.out.println("移除周五后的假期: " + holidays);
        //移除周五后的假期: [SATURDAY, SUNDAY]

        // 9. 集合操作:判断包含关系
        boolean hasSaturday = weekend.contains(Day.SATURDAY);
        System.out.println("周末包含周六吗?" + hasSaturday);
        //周末包含周六吗?true

        // 10. 集合操作:求交集
        EnumSet<Day> intersection = EnumSet.copyOf(workDays);
        intersection.retainAll(someDays); // 与周一、周三、周五的交集
        System.out.println("工作日与{周一,周三,周五}的交集: " + intersection);
        //工作日与{周一,周三,周五}的交集: [MONDAY, WEDNESDAY, FRIDAY]

        // 11. 遍历EnumSet
        System.out.println("\n遍历所有星期:");
        for (Day day : allDays) {
            System.out.println(day.getOrder() + ": " + day +
                    (day.isWeekday() ? " (工作日)" : " (周末)"));
        }
        //遍历所有星期:
        //1: MONDAY (工作日)
        //2: TUESDAY (工作日)
        //3: WEDNESDAY (工作日)
        //4: THURSDAY (工作日)
        //5: FRIDAY (工作日)
        //6: SATURDAY (周末)
        //7: SUNDAY (周末)
    }
}

EnumSet 原理分析

以下是 EnumSet 的部分源码,查看成员变量定义和构造方法定义:

public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
    implements Cloneable, java.io.Serializable {
    
    // 枚举的类型信息
    final Class<E> elementType;
    // 枚举常量数组(缓存,避免多次调用Enum.values())
    final Enum<?>[] universe;
    
    // 私有构造方法,供子类调用
    EnumSet(Class<E> elementType, Enum<?>[] universe) {
        this.elementType = elementType;
        this.universe = universe;
    }
    // ... 其他方法
}

注意,EnumSet 本身是抽象类,实际上通过两个子类实现,如下:

  • RegularEnumSet  适用于枚举常量数量 ≤ 64 的情况,用 long 存储位向量。

  • JumboEnumSet  适用于枚举常量数量 > 64 的情况,用 long[] 存储位向量。

创建空 EnumSet

通过 noneOf(Class<E> elementType)  方法可以创建指定枚举类型的空 EnumSet,方法定义如下:

/**
 * 创建一个指定枚举类型的空 EnumSet
 * 该方法是 EnumSet 的核心静态工厂方法之一,负责根据枚举常量数量自动选择最优实现类。
 * 
 * @param <E> 集合中元素的类型,必须是 Enum 的子类(即枚举类型)
 * @param elementType 目标 EnumSet 要存储的枚举类型的 Class 对象
 * @return 一个指定枚举类型的空 EnumSet 实例(具体是 RegularEnumSet 或 JumboEnumSet)
 * @throws NullPointerException 如果传入的 elementType 为 null(无法确定枚举类型)
 */
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
    // 1. 获取指定枚举类型的所有常量数组
    // 该数组是 JDK 内部缓存的,避免每次调用 Enum.values() 创建新数组,提升性能
    Enum<?>[] universe = getUniverse(elementType);
    
    // 2. 校验 elementType 是否为合法枚举类型
    // 若 universe 为 null,说明 elementType 不是枚举类,抛出类型转换异常
    if (universe == null)
        throw new ClassCastException(elementType + " not an enum");

    // 3. 根据枚举常量数量选择对应的 EnumSet 实现类
    // 当常量数量 <= 64 时,使用 RegularEnumSet(基于单个 long 位向量存储,更高效)
    // 当常量数量 > 64 时,使用 JumboEnumSet(基于 long 数组存储,支持更多元素)
    if (universe.length <= 64)
        return new RegularEnumSet<>(elementType, universe);
    else
        return new JumboEnumSet<>(elementType, universe);
}

/**
 * 获取指定枚举类型 E 的所有常量组成的数组
 * 
 * @param <E> 目标枚举类型,必须是 Enum 的子类
 * @param elementType 要获取常量数组的枚举类型的 Class 对象
 * @return 该枚举类型所有常量组成的数组(缓存的共享实例)
 *         若 elementType 不是枚举类型,返回 null
 */
private static <E extends Enum<E>> E[] getUniverse(Class<E> elementType) {
    // 1. 通过 SharedSecrets 获取 Java 语言内部访问器(JavaLangAccess)
    // SharedSecrets 是 JDK 内部机制,用于在不暴露 public API 的情况下,
    // 让核心类(如 EnumSet)访问JDK内部功能
    //
    // 2. 调用 JavaLangAccess 的 getEnumConstantsShared 方法,获取枚举类型的缓存常量数组
    // 该方法返回的数组是枚举类初始化时创建并缓存的单例数组,所有调用者共享同一实例,
    // 避免重复克隆数组的性能消耗
    return SharedSecrets.getJavaLangAccess().getEnumConstantsShared(elementType);
}

创建包含枚举所有常量 EnumSet

通过 allOf(Class<E> elementType) 方法,创建枚举类型 elementType 所有常量的 EnumSet 实例,相当于调用枚举的 values() 方法,将返回的枚举常量数组存入 EnumSet:

/**
 * 创建一个包含指定枚举类型所有常量的 EnumSet
 * 即集合内容与该枚举类型的完整常量列表完全一致(枚举有多少个常量,集合就包含多少个元素)
 * 
 * @param <E> 集合中元素的类型,必须是 Enum 的子类(枚举类型)
 * @param elementType 目标 EnumSet 要包含的枚举类型的 Class 对象
 * @return 包含该枚举类型所有常量的 EnumSet 实例
 * @throws NullPointerException 如果传入的 elementType 为 null(无法确定枚举类型及常量列表)
 */
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) {
    // 1. 先通过 noneOf() 创建指定枚举类型的空 EnumSet
    // 注意:noneOf() 会自动根据枚举常量数量选择 RegularEnumSet 或 JumboEnumSet
    // 确保底层存储适配
    EnumSet<E> result = noneOf(elementType);
    
    // 2. 调用 EnumSet 的抽象方法 addAll(),向空集合中添加该枚举类型的所有常量
    // 注意:addAll() 的具体实现由子类(RegularEnumSet 或 JumboEnumSet)提供
    // RegularEnumSet 通过位运算将所有位设为 1,批量添加所有常量
    // JumboEnumSet 通过遍历 long 数组,将所有数组元素的位设为 1,批量添加所有常量
    result.addAll();
    
    return result;
}

创建初始枚举元素 EnumSet

通过 of(E e) 方法以及重载的 of() 方法,根据传递的初始值创建 EnumSet,重载方法签名如下:

Java 集合:EnumSet 类

源码定义如下:

/**
 * 创建一个初始包含指定单个枚举元素的 EnumSet
 * 
 * 注:本方法是一系列重载方法之一,分别支持初始化包含 1 到 5 个元素的 EnumSet。
 * 第六个重载版本使用可变参数(varargs)特性,可创建包含任意数量元素的 EnumSet,
 * 但相比非可变参数的重载版本,其运行速度可能较慢(因可变参数存在数组创建开销)。
 * 
 * @param <E> 指定元素的类型及集合的元素类型(必须是枚举类型)
 * @param e 集合初始要包含的单个元素
 * @throws NullPointerException 如果传入的元素 e 为 null(EnumSet 不允许 null 元素)
 * @return 初始包含指定元素的 EnumSet 实例
 */
public static <E extends Enum<E>> EnumSet<E> of(E e) {
    // 1. 通过元素 e 的枚举类型(e.getDeclaringClass())创建空EnumSet
    // getDeclaringClass() 返回该枚举常量所属的枚举类 Class 对象,确保集合类型与元素类型一致
    EnumSet<E> result = noneOf(e.getDeclaringClass());
    
    // 2. 向空集合中添加指定元素e
    // add() 方法由具体子类(RegularEnumSet/JumboEnumSet)实现,通过位运算标记元素存在
    result.add(e);
    
    return result;
}

创建范围内枚举元素 EnumSet

通过 range(E from, E to) 方法创建一个初始元素包含指定两个端点(from~to)所定义范围内所有元素的EnumSet。

注意,返回的 EnumSet 会包含两个端点元素本身,若两个端点相同(from == to),则集合只包含该单个元素。但两个端点必须符合顺序要求(from 不能在枚举定义中位于 to 之后)。

源码定义如下:

/**
 * 创建一个初始包含指定两个端点所定义范围内所有元素的 EnumSet
 * 
 * @param <E> 参数元素及集合的元素类型(必须是枚举类型)
 * @param from 范围的起始端点元素(包含在集合中)
 * @param to 范围的结束端点元素(包含在集合中)
 * @throws NullPointerException 如果 from 或 to 为 null(EnumSet 不允许 null元素)
 * @throws IllegalArgumentException 如果 from 在枚举顺序中位于 to 之后(from.compareTo(to) > 0)
 * @return 包含 from 到 to(含两端点)所有枚举元素的 EnumSet 实例
 */
public static <E extends Enum<E>> EnumSet<E> range(E from, E to) {
    // 1. 校验范围的合法性:确保from不位于to之后
    // 通过枚举的 compareTo 方法比较顺序(基于枚举常量的定义顺序)
    if (from.compareTo(to) > 0)
        throw new IllegalArgumentException(from + " > " + to);
    
    // 2. 根据 from 所属的枚举类型创建空 EnumSet
    EnumSet<E> result = noneOf(from.getDeclaringClass());
    
    // 3. 调用抽象方法 addRange(),向空集合中添加 from 到 to 范围内的所有元素
    // 具体实现由子类(RegularEnumSet 或 JumboEnumSet)提供:
    // (1)利用枚举常量的 ordinal() 值连续性,通过位运算批量添加范围内元素
    // (2)比逐个调用add()方法更高效,尤其对于大范围元素场景
    result.addRange(from, to);
    
    return result;
}

上面介绍了如何通过 EnumSet 的 noneOf()、allOf()、of()、range() 方法去创建实例,但是将枚举元素添加到 EnumSet 均是通过抽象方法 addRange()、add()、addAll() 完成,由具体子类去实现,下面将介绍 RegularEnumSet 和 JumboEnumSet 具体实现类。

  

RegularEnumSet 类

RegularEnumSet 是 Java 集合框架中 EnumSet 的一个私有实现类,专门用于处理元素数量不超过 64 个的枚举类型("常规大小" 的枚举)。它通过位运算实现高效的集合操作,是枚举集合的核心实现之一。

类定义如下:

class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
    @java.io.Serial
    private static final long serialVersionUID = 3411599620347842686L;
    
    /**
     * 此集合的位向量表示。第 2^k 位表示 universe[k] 存在于此集合中。
     * 使用 long 类型的位运算来高效存储枚举元素,每个位对应一个枚举常量
     * 例如,bit 0 对应枚举中的第一个元素,bit 1 对应第二个元素,以此类推
     */
    private long elements = 0L;

    /**
     * 创建一个指定枚举类型和枚举常量数组的 RegularEnumSet
     * @param elementType 枚举类型的 Class 对象
     * @param universe 包含所有枚举常量的数组(枚举的完整值集合)
     */
    RegularEnumSet(Class<E> elementType, Enum<?>[] universe) {
        super(elementType, universe);
    }

    /**
     * 添加从 from 到 to(包括两者)的所有枚举元素到集合中
     * 使用位运算高效设置范围内的所有位。
     *
     * @param from 起始枚举元素(包含)
     * @param to 结束枚举元素(包含)
     */
    void addRange(E from, E to) {
        // 位运算逻辑解析:
        // 1. (from.ordinal() - to.ordinal() - 1) 计算需要移位的位数
        // 2. -1L >>> [位数] 生成从0到指定位置的连续1的位掩码
        // 3. << from.ordinal() 将生成的位掩码左移到起始位置
        // 最终结果:from 到 to 之间的所有位都被设置为1
        // 例如:将 2 ~ 6 之间的位设置为1,以 int 为例
        // -1 => 1111 1111 1111 1111 1111 1111 1111 1111
        // -1 >>> (2 - 6 -1) >>> -5 >>> -5%32 >>> 27
        // -1 >>> 27 => 0000 0000 0000 0000 0000 0000 0001 1111
        // << 2 => 0000 0000 0000 0000 0000 0000 0111 1100
        elements = (-1L >>>  (from.ordinal() - to.ordinal() - 1)) << from.ordinal();
    }

    /**
     * 将所有可能的枚举元素添加到集合中(即包含枚举类型的所有常量)
     */
    void addAll() {
        // 如果枚举常量数组不为空
        if (universe.length != 0)
            // 生成一个前 universe.length 位都为 1 的位掩码
            // 当 universe.length <= 64 时,-1L >>> -universe.length 
            // 等价于 
            // (1L << universe.length) - 1
            elements = -1L >>> -universe.length;
    }

    //...省略...
}

注意:

(1)ordinal() 是 Enum 类的一个内置方法,用于返回枚举常量在其枚举声明中的位置索引(从 0 开始)。

(2)在 Java 中,>>> 是无符号右移运算符,它的作用是将一个整数的二进制位向右移动指定的位数,且高位始终用 0 填充(无论原数是正数还是负数)。

当 >>> 移动的位数是负数时,Java 会对这个负数进行特殊处理:将移动位数对 32(对于 int 类型)或 64(对于 long 类型)取模,最终使用模运算的结果作为实际移动位数。

具体规则:

  • 对于 int 类型(32 位)若移动位数为 -n,实际移动位数为 -n % 32。例如:a >>> -1 等价于 a >>> 31(因为 -1 % 32 = 31)。

  • 对于 long 类型(64 位)若移动位数为 -n,实际移动位数为 -n % 64。例如:b >>> -2 等价于 b >>> 62(因为 -2 % 64 = 62)。

从集合删除元素的源码如下:

/**
 * 从集合中移除指定的元素(如果该元素存在)
 *
 * @param e 要从集合中移除的元素(如果存在)
 * @return 如果集合中包含该元素并成功移除,则返回 true;否则返回 false
 */
public boolean remove(Object e) {
    // 枚举集合中不允许存在null元素,直接返回false
    if (e == null)
        return false;
    
    // 获取元素的类对象,用于类型检查
    Class<?> eClass = e.getClass();
    // 检查元素类型是否与当前集合的枚举类型一致
    // 若元素的类不是当前枚举类型,且其超类也不是当前枚举类型,则不是该集合中的元素
    if (eClass != elementType && eClass.getSuperclass() != elementType)
        return false;

    // 保存当前的元素位向量,用于后续判断是否发生变化
    long oldElements = elements;
    // 通过位运算移除元素,核心逻辑:
    // 1. ((Enum<?>)e).ordinal() 获取该枚举元素的序号
    // 2. 1L << 序号:生成只有该元素对应位为1的位掩码
    // 3. ~运算:对上述位掩码取反,得到只有该元素对应位为0、其他位为1的掩码
    // 4. elements & 上述结果:将 elements 中该元素对应的位设为 0(即移除该元素)
    elements &= ~(1L << ((Enum<?>)e).ordinal());
    
    // 若移除前后的位向量不同,说明成功移除了元素,返回 true;否则返回 false
    return elements != oldElements;
}

到这里,RegularEnumSet 中新增和删除的方法就介绍完了,更多方法实现读者自行阅读。

  

JumboEnumSet 类

JumboEnumSet 是 Java 集合框架中 EnumSet 的另一个私有实现类,专门用于处理元素数量超过 64 个的枚举类型("大型" 枚举)。它与 RegularEnumSet 共同构成了枚举集合的完整实现,根据枚举常量的数量自动选择合适的实现类。

当枚举类型包含的常量数量 超过 64 个 时,EnumSet 的静态工厂方法(如 EnumSet.of()、EnumSet.allOf())会自动返回 JumboEnumSet 实例。这是因为 RegularEnumSet 基于 long 类型的位向量(仅能表示 64 位),无法处理更多元素。

JumboEnumSet 内部使用 long[] 数组作为位向量存储元素,数组中的每个 long 元素可表示 64 个枚举常量:

  • 数组索引 i 对应的 long 值,负责存储序号范围为 [i×64, (i+1)×64 - 1] 的枚举常量。

  • 若第 k 位为 1,表示集合包含对应序号的枚举元素。

类定义如下:

class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
    @java.io.Serial
    private static final long serialVersionUID = 334349849919042784L;

    /**
     * 此集合的位向量表示。数组中第j个元素的第i位表示
     * universe[64*j + i]是否存在于该集合中
     * 说明:使用 long 数组分段存储位向量,每个 long 处理64个枚举元素
     */
    private long elements[];

    /**
     * 集合中元素的数量
     */
    private int size = 0;

    /**
     * 创建指定枚举类型和枚举常量数组的 JumboEnumSet
     * 
     * @param elementType 枚举类型的Class对象
     * @param universe 包含所有枚举常量的完整数组
     */
    JumboEnumSet(Class<E> elementType, Enum<?>[] universe) {
        super(elementType, universe);
        // 计算存储所有枚举元素所需的long数组长度
        // (universe.length + 63) >>> 6 等价于 (universe.length + 63) / 64
        // 确保数组长度能容纳所有枚举元素
        elements = new long[(universe.length + 63) >>> 6];
    }

    /**
     * 添加从 from 到 to(包括两者)的所有枚举元素到集合中
     * 
     * @param from 起始枚举元素(包含)
     * @param to 结束枚举元素(包含)
     */
    void addRange(E from, E to) {
        // 计算起始元素在long数组中的索引(ordinal / 64)
        int fromIndex = from.ordinal() >>> 6;
        // 计算结束元素在long数组中的索引(ordinal / 64)
        int toIndex = to.ordinal() >>> 6;

        // 如果起始和结束元素在同一个long数组元素中
        if (fromIndex == toIndex) {
            // 生成从from到to的连续位掩码并设置
            elements[fromIndex] = (-1L >>>  (from.ordinal() - to.ordinal() - 1))
                            << from.ordinal();
        } else {
            // 处理起始元素所在的long:从from开始到该long末尾的所有位设为1
            elements[fromIndex] = (-1L << from.ordinal());
            // 处理中间的所有long:全部设为1(表示所有64位都有元素)
            for (int i = fromIndex + 1; i < toIndex; i++)
                elements[i] = -1;
            // 处理结束元素所在的long:从开始到to的所有位设为1
            elements[toIndex] = -1L >>> (63 - to.ordinal());
        }
        // 更新集合大小
        size = to.ordinal() - from.ordinal() + 1;
    }

    /**
     * 将所有可能的枚举元素添加到集合中(即包含枚举类型的所有常量)
     */
    void addAll() {
        // 先将所有long元素都设为-1(二进制全为1)
        for (int i = 0; i < elements.length; i++)
            elements[i] = -1;
        // 对最后一个long元素进行调整,只保留有效位数(枚举元素实际数量可能不是64的整数倍)
        elements[elements.length - 1] >>>= -universe.length;
        // 更新集合大小为枚举元素总数
        size = universe.length;
    }

    //...省略...
}

从集合删除元素的源码如下:

/**
 * 从集合中移除指定的元素(如果该元素存在)
 *
 * @param e 要从集合中移除的元素(如果存在)
 * @return 如果集合中包含该元素并成功移除,则返回{@code true};否则返回{@code false}
 */
public boolean remove(Object e) {
    // 枚举集合中不允许存在null元素,直接返回false
    if (e == null)
        return false;
    
    // 获取元素的类对象,用于类型检查
    Class<?> eClass = e.getClass();
    // 检查元素类型是否与当前集合的枚举类型一致
    // 若元素的类不是当前枚举类型,且其超类也不是当前枚举类型,则不是该集合中的元素
    if (eClass != elementType && eClass.getSuperclass() != elementType)
        return false;
    
    // 获取元素在枚举中的序号
    int eOrdinal = ((Enum<?>)e).ordinal();
    // 计算该元素在long数组中的索引(ordinal / 64)
    int eWordNum = eOrdinal >>> 6;

    // 保存当前的long值,用于后续判断是否发生变化
    long oldElements = elements[eWordNum];
    // 核心逻辑:
    // 1. 1L << eOrdinal:生成只有该元素对应位为 1 的位掩码
    // 2. ~运算:对上述位掩码取反
    // 3. elements[eWordNum] & 上述结果:将该元素对应的位设为0(即移除该元素)
    elements[eWordNum] &= ~(1L << eOrdinal);
    
    // 判断是否成功移除元素
    boolean result = (elements[eWordNum] != oldElements);
    // 如果成功移除,更新集合大小
    if (result)
        size--;
    return result;
}

更多信息请参考 https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html  API 文档。

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号