一、枚举类型——用EnumSet来代替标识

Set 是一种不允许有重复元素存在的集合。enum 要求每个内部成员都是唯一的,因此看起来很像 Set,但是由于无法添加或移除元素,它并不如 Set 那么好用。于是 EnumSet 被引入,用来配合 enum 的使用,以替代传统的基于 int 的“位标识”用法。

这种标识通常被用来表明某些开-关状态信息,但最终你实际上是在操作各种位状态,而不是业务逻辑,所以非常容易写出难以理解的代码。

性能是 EnumSet 的设计目标之一,因为它需要和位标识竞争(位操作的性能通常远高于 HashSet)。其内部实现其实是一个被用作位数组的long型变量,所以它非常高效。

这样做的好处是,你现在拥有了一种更具表现力的方式来表达二进制特征的存在与否,而且无须担心性能。

EnumSet 中的元素必须来自某个枚举类型。下面是一个假想的例子,使用枚举来表示一栋大楼中警报感应器的安装位置:

AlarmPoints.java

public enum AlarmPoints {
    STAIR1, STAIR2, LOBBY, OFFICE1, OFFICE2, OFFICE3,
    OFFICE4, BATHROOM, UTILITY, KITCHEN
}

EnumSets.java

import java.util.EnumSet;

import static enums.TEST0523.AlarmPoints.*;

public class EnumSets {
    public static void main(String[] args) {
        // Empty
        EnumSet<AlarmPoints> points = EnumSet.noneOf(AlarmPoints.class);
        points.add(BATHROOM);
        System.out.println(points);
        points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
        System.out.println(points);
        points = EnumSet.allOf(AlarmPoints.class);
        points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
        System.out.println(points);
        points.removeAll(EnumSet.range(OFFICE1, OFFICE4));
        System.out.println(points);
        points = EnumSet.complementOf(points);
        System.out.println(points);
    }
}

运行结果如下:
在这里插入图片描述

静态导入(static import)的作用是简化枚举常量的使用。方法名的自解释性相当好,你还可以在 JDK 的文档里找到完整的细节。如果你仔细看文档,会发现 of() 方法分别以可变参数和接收 3~5 个显式参数的方式进行了重载。

这体现了 EnumSet 对性能的关注,因为本来只需要一个接收可变参数的 of() 方法就能解决问题,但是这样会比通过显式参数的方式略为低效。因此,如果用 2~5 个参数来调用 of() 方法,实际起作用的是显式调用(速度稍快);如果用1个或5个以上的参数来调用,则会是可变参数的版本。

注意,如果用1个参数来调用,编译器并不会构建可变参数数组,因此这种情况下不会产生额外的开销。

EnumSet 是基于64位的 long 构建的,每个枚举实例需要占用1位来表达是否存在的状态,这意味着在单个 long 的支撑范围内,1个 Enumset 最多可支持包含64个元素的枚举类型。如果枚举类型中的元素超过了64个,会发生什么呢?

BigEnumSet.java

import java.util.EnumSet;

public class BigEnumSet {
    enum Big {
        A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
        A10, A11, A12, A13, A14, A15, A16, A17, A18, A19,
        A20, A21, A22, A23, A24, A25, A26, A27, A28, A29,
        A30, A31, A32, A33, A34, A35, A36, A37, A38, A39,
        A40, A41, A42, A43, A44, A45, A46, A47, A48, A49,
        A50, A51, A52, A53, A54, A55, A56, A57, A58, A59,
        A60, A61, A62, A63, A64, A65, A66, A67, A68, A69,
        A70, A71, A72, A73, A74, A75
    }

    public static void main(String[] args) {
        EnumSet<Big> bigEnumSet = EnumSet.allOf(Big.class);
        System.out.println(bigEnumSet);
    }
}

运行结果如下:
在这里插入图片描述

显然 EnumSet 可以支持包含超过64个元素的枚举类型,所以我们可以推测,它在必要的时候会引入新的 long 型变量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一只小熊猫呀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值