在Java的集合体系中,EnumSet是专门为枚举类型设计的集合实现,它内部采用位向量的方式存储元素,在性能和使用效率上都有独特的优势。下面我们先看一张示意图,直观了解EnumSet的基本定位。

EnumSet的基本特性
EnumSet的所有元素都必须是同一枚举类型的实例,它不允许添加null元素,否则会抛出NullPointerException。EnumSet的内部实现基于位向量,每个枚举常量对应一个位,因此它的存储和查询效率都非常高,时间复杂度基本是O(1)。和普通的Set实现相比,EnumSet在遍历、添加、删除枚举元素时性能更优,而且内存占用更低。
创建EnumSet的常用方式
EnumSet提供了多个静态方法来创建实例,不能直接通过构造器创建,常用的创建方法如下:
1. 创建包含指定枚举值的集合
使用of方法可以创建包含指定枚举值的EnumSet,支持传入1到5个枚举值,或者传入数组形式的枚举值。
// 定义枚举类型
enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class EnumSetDemo {
public static void main(String[] args) {
// 创建包含指定枚举值的EnumSet
EnumSet<Day> weekdays = EnumSet.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY, Day.THURSDAY, Day.FRIDAY);
System.out.println("工作日集合:" + weekdays);
// 传入数组创建EnumSet
Day[] restDays = {Day.SATURDAY, Day.SUNDAY};
EnumSet<Day> rest = EnumSet.of(restDays[0], restDays[1]);
System.out.println("休息日集合:" + rest);
}
}2. 创建空集合或全量集合
可以使用noneOf创建指定枚举类型的空集合,使用allOf创建包含该枚举所有值的集合。
public class EnumSetDemo2 {
public static void main(String[] args) {
// 创建空的Day类型EnumSet
EnumSet<Day> emptySet = EnumSet.noneOf(Day.class);
System.out.println("空集合:" + emptySet);
// 创建包含Day所有枚举值的集合
EnumSet<Day> allDays = EnumSet.allOf(Day.class);
System.out.println("所有日期集合:" + allDays);
}
}3. 创建范围集合
使用range方法可以创建包含枚举值范围内所有元素的集合,范围包含起始和结束的枚举值。
public class EnumSetDemo3 {
public static void main(String[] args) {
// 创建MONDAY到FRIDAY范围的EnumSet
EnumSet<Day> rangeSet = EnumSet.range(Day.MONDAY, Day.FRIDAY);
System.out.println("范围集合:" + rangeSet);
}
}EnumSet的常用操作
EnumSet支持普通的Set操作,同时也提供了一些针对枚举集合的便捷操作:
- 添加元素:使用
add方法添加单个枚举值,addAll添加另一个集合的所有元素。 - 删除元素:使用
remove删除指定元素,removeAll删除集合中包含的元素。 - 补集操作:使用
complementOf可以获取当前集合的补集,也就是枚举类型中不在当前集合的所有元素。
public class EnumSetOperationDemo {
public static void main(String[] args) {
EnumSet<Day> weekdays = EnumSet.range(Day.MONDAY, Day.FRIDAY);
// 添加元素
weekdays.add(Day.SATURDAY);
System.out.println("添加后:" + weekdays);
// 删除元素
weekdays.remove(Day.SATURDAY);
System.out.println("删除后:" + weekdays);
// 获取补集
EnumSet<Day> complement = EnumSet.complementOf(weekdays);
System.out.println("工作日补集(休息日):" + complement);
}
}EnumSet的适用场景
EnumSet非常适合以下场景:
- 需要存储和操作同一枚举类型的多个值,比如权限集合、状态集合等。
- 需要频繁对枚举集合进行添加、删除、查询、遍历操作,追求性能的场景。
- 需要快速计算枚举集合的补集、交集、并集等操作的场景。
需要注意的是,EnumSet不是线程安全的,如果需要在多线程环境下使用,需要手动进行同步处理,或者使用Collections.synchronizedSet包装后再使用。
EnumSet和HashSet的对比
我们可以通过下面的表格直观对比两者的差异:
| 对比项 | EnumSet | HashSet |
|---|---|---|
| 元素类型 | 仅支持同一枚举类型的实例 | 支持任意类型的对象 |
| 存储方式 | 位向量,内存占用低 | 哈希表,内存占用较高 |
| 性能 | 添加、删除、查询、遍历效率都更高 | 性能相对更低 |
| null元素 | 不允许,添加会抛异常 | 允许添加一个null元素 |
在实际开发中,如果处理的集合元素都是同一枚举类型,优先选择EnumSet可以获得更好的性能表现。