在Java中,EnumSet是专门为枚举类型设计的集合实现,它内部采用位向量存储元素,在性能和内存占用上都有明显优势,非常适合用来管理枚举类型的集合数据。相比使用HashSet存储枚举元素,EnumSet的增删改查操作时间复杂度接近O(1),且内存占用更小,是枚举集合场景的首选方案。

EnumSet的核心特性
EnumSet有以下几个核心特性,理解这些特性有助于我们更好地使用它:
- 只能存储同一种枚举类型的元素,创建时需要指定枚举类
- 内部基于位向量实现,元素数量不超过64个时,使用单个long存储,超过64个时使用long数组存储
- 不允许存储null元素,尝试添加null会抛出NullPointerException
- 迭代顺序按照枚举常量的声明顺序排列
- 大部分操作的时间复杂度都是常数级别,性能优于普通的Set实现
EnumSet的创建方式
EnumSet提供了多种静态工厂方法来创建实例,我们不需要使用构造方法,而是通过以下方式创建:
1. 创建空集合
使用noneOf方法可以创建一个指定枚举类型的空EnumSet:
import java.util.EnumSet;
// 定义一个枚举类型
enum WeekDay {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class EnumSetDemo {
public static void main(String[] args) {
// 创建空的WeekDay类型EnumSet
EnumSet<WeekDay> emptySet = EnumSet.noneOf(WeekDay.class);
System.out.println("空EnumSet: " + emptySet);
}
}
2. 创建包含所有枚举值的集合
使用allOf方法可以创建一个包含指定枚举类型所有常量的EnumSet:
import java.util.EnumSet;
enum WeekDay {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class EnumSetDemo {
public static void main(String[] args) {
// 创建包含WeekDay所有枚举值的EnumSet
EnumSet<WeekDay> allSet = EnumSet.allOf(WeekDay.class);
System.out.println("全量EnumSet: " + allSet);
}
}
3. 创建包含指定枚举值的集合
使用of方法可以创建包含指定枚举值的EnumSet,支持传入1到5个枚举值,也支持传入数组:
import java.util.EnumSet;
enum WeekDay {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class EnumSetDemo {
public static void main(String[] args) {
// 传入多个枚举值创建集合
EnumSet<WeekDay> workdaySet = EnumSet.of(WeekDay.MONDAY, WeekDay.TUESDAY, WeekDay.WEDNESDAY, WeekDay.THURSDAY, WeekDay.FRIDAY);
System.out.println("工作日集合: " + workdaySet);
// 传入数组创建集合
WeekDay[] restDays = {WeekDay.SATURDAY, WeekDay.SUNDAY};
EnumSet<WeekDay> restSet = EnumSet.of(restDays[0], restDays[1]);
System.out.println("休息日集合: " + restSet);
}
}
4. 创建指定范围枚举值的集合
使用range方法可以创建包含从起始枚举值到结束枚举值范围内所有枚举常量的EnumSet,范围是闭区间:
import java.util.EnumSet;
enum WeekDay {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class EnumSetDemo {
public static void main(String[] args) {
// 创建周一到周五的集合
EnumSet<WeekDay> rangeSet = EnumSet.range(WeekDay.MONDAY, WeekDay.FRIDAY);
System.out.println("范围枚举集合: " + rangeSet);
}
}
EnumSet的常用操作
EnumSet支持普通Set的所有操作,同时还有一些针对枚举场景的便捷方法:
1. 添加与删除元素
可以使用add方法添加元素,remove方法删除元素:
import java.util.EnumSet;
enum WeekDay {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class EnumSetDemo {
public static void main(String[] args) {
EnumSet<WeekDay> set = EnumSet.noneOf(WeekDay.class);
// 添加元素
set.add(WeekDay.MONDAY);
set.add(WeekDay.TUESDAY);
System.out.println("添加后集合: " + set);
// 删除元素
set.remove(WeekDay.MONDAY);
System.out.println("删除后集合: " + set);
}
}
2. 批量操作
EnumSet支持补集、并集、交集等批量操作,对应方法为complementOf、copyOf等:
import java.util.EnumSet;
enum WeekDay {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class EnumSetDemo {
public static void main(String[] args) {
EnumSet<WeekDay> workdaySet = EnumSet.of(WeekDay.MONDAY, WeekDay.TUESDAY, WeekDay.WEDNESDAY, WeekDay.THURSDAY, WeekDay.FRIDAY);
// 获取补集,即休息日集合
EnumSet<WeekDay> restSet = EnumSet.complementOf(workdaySet);
System.out.println("工作日补集(休息日): " + restSet);
// 复制集合
EnumSet<WeekDay> copySet = EnumSet.copyOf(workdaySet);
System.out.println("复制的工作日集合: " + copySet);
}
}
3. 遍历操作
EnumSet的迭代顺序是枚举常量的声明顺序,遍历方式和普通Set一致:
import java.util.EnumSet;
enum WeekDay {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class EnumSetDemo {
public static void main(String[] args) {
EnumSet<WeekDay> set = EnumSet.allOf(WeekDay.class);
System.out.println("遍历所有枚举值:");
for (WeekDay day : set) {
System.out.println(day);
}
}
}
EnumSet使用注意事项
- EnumSet是非线程安全的,如果需要在多线程环境下使用,需要通过
Collections.synchronizedSet包装,或者手动加锁保证线程安全 - 不要在EnumSet中尝试添加null元素,会直接抛出NullPointerException
- EnumSet的所有操作都依赖于枚举类型的声明,如果枚举类型的常量发生变化,需要同步更新使用EnumSet的代码
- 当枚举常量数量超过64个时,EnumSet依然可以正常工作,只是内部存储会切换为long数组,性能依然优于普通Set
适用场景
EnumSet非常适合以下场景:
- 需要存储一组枚举常量,比如权限集合、状态集合、配置项集合等
- 需要对枚举集合进行批量操作,比如取补集、判断包含关系等
- 对集合操作的性能和内存占用有较高要求的场景
如果我们的集合中存储的不是枚举类型,那么不能使用EnumSet,只能选择HashSet、TreeSet等其他Set实现。在枚举集合的场景下,优先选择EnumSet可以获得更好的性能和更简洁的代码。