1. 概述
EnumSet是Java中专门为枚举类型设计的高性能Set实现,它使用位向量(bit vector)在内部表示枚举集合,性能接近位标志操作。
2. 核心特性
2.1 基本特点
- 专为枚举类型优化
- 非线程安全
- 不允许null元素
- 迭代顺序遵循枚举声明顺序
- 内存高效,使用位运算
- 所有基本操作都在常数时间内完成
2.2 内部实现
java
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable {
// 底层使用long数组存储
// 对于64个枚举常量以内的枚举,使用单个long
// 超过64个,使用long数组
final Class<E> elementType; // 枚举类型
final Enum<?>[] universe; // 所有枚举常量
}
3. 创建EnumSet的工厂方法
3.1 常用工厂方法
java
// 定义枚举类型
enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY,
FRIDAY, SATURDAY, SUNDAY
}
enum Color {
RED, GREEN, BLUE, YELLOW, BLACK, WHITE
}
// 1. 创建空集合
EnumSet<Day> emptySet = EnumSet.noneOf(Day.class);
// 2. 包含所有枚举值的集合
EnumSet<Day> allDays = EnumSet.allOf(Day.class);
// 3. 从指定枚举常量创建
EnumSet<Day> workDays = EnumSet.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY);
EnumSet<Day> singleDay = EnumSet.of(Day.MONDAY); // 单个元素
// 4. 创建范围集合
EnumSet<Day> weekStart = EnumSet.range(Day.MONDAY, Day.FRIDAY);
// 5. 从其他集合创建
Set<Day> otherSet = new HashSet<>(Arrays.asList(Day.MONDAY, Day.TUESDAY));
EnumSet<Day> fromCollection = EnumSet.copyOf(otherSet);
EnumSet<Day> fromEnumSet = EnumSet.copyOf(workDays);
4. 常用操作详解
4.1 添加和删除
java
EnumSet<Day> days = EnumSet.noneOf(Day.class);
// 添加元素
days.add(Day.MONDAY);
days.addAll(EnumSet.of(Day.TUESDAY, Day.WEDNESDAY));
// 删除元素
days.remove(Day.MONDAY);
days.removeAll(EnumSet.of(Day.TUESDAY, Day.WEDNESDAY));
// 批量操作
EnumSet<Day> weekend = EnumSet.of(Day.SATURDAY, Day.SUNDAY);
days.addAll(weekend); // 添加周末
days.retainAll(weekend); // 只保留周末
days.removeAll(weekend); // 移除周末
4.2 查询操作
java
EnumSet<Day> days = EnumSet.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY);
// 基本查询
boolean isEmpty = days.isEmpty(); // false
int size = days.size(); // 3
boolean contains = days.contains(Day.MONDAY); // true
// 包含关系
EnumSet<Day> workDays = EnumSet.range(Day.MONDAY, Day.FRIDAY);
boolean containsAll = workDays.containsAll(days); // true
// 比较
EnumSet<Day> other = EnumSet.of(Day.MONDAY, Day.TUESDAY);
boolean equals = days.equals(other); // false
4.3 集合运算
java
EnumSet<Day> set1 = EnumSet.of(Day.MONDAY, Day.TUESDAY, Day.WEDNESDAY);
EnumSet<Day> set2 = EnumSet.of(Day.WEDNESDAY, Day.THURSDAY, Day.FRIDAY);
// 并集
EnumSet<Day> union = EnumSet.copyOf(set1);
union.addAll(set2); // 周一、周二、周三、周四、周五
// 交集
EnumSet<Day> intersection = EnumSet.copyOf(set1);
intersection.retainAll(set2); // 周三
// 差集
EnumSet<Day> difference = EnumSet.copyOf(set1);
difference.removeAll(set2); // 周一、周二
// 对称差(异或)
EnumSet<Day> complement = EnumSet.copyOf(set1);
complement.removeAll(set2); // 先从set1中移除set2
EnumSet<Day> temp = EnumSet.copyOf(set2);
temp.removeAll(set1); // 再从set2中移除set1
complement.addAll(temp); // 合并:周一、周二、周四、周五
5. 实际应用示例
5.1 权限管理系统
java
enum Permission {
READ, WRITE, EXECUTE, DELETE, ADMIN
}
class User {
private String name;
private EnumSet<Permission> permissions;
public User(String name, Permission... perms) {
this.name = name;
this.permissions = EnumSet.noneOf(Permission.class);
this.permissions.addAll(Arrays.asList(perms));
}
public void addPermission(Permission perm) {
permissions.add(perm);
}
public void removePermission(Permission perm) {
permissions.remove(perm);
}
public boolean hasPermission(Permission perm) {
return permissions.contains(perm);
}
public boolean hasAllPermissions(Permission... perms) {
return permissions.containsAll(Arrays.asList(perms));
}
public boolean isAdmin() {
return permissions.contains(Permission.ADMIN);
}
public EnumSet<Permission> getEffectivePermissions() {
if (isAdmin()) {
return EnumSet.allOf(Permission.class);
}
return permissions.clone();
}
// 使用示例
public static void main(String[] args) {
User user = new User("Alice", Permission.READ, Permission.WRITE);
System.out.println("Can read? " + user.hasPermission(Permission.READ));
System.out.println("Can delete? " + user.hasPermission(Permission.DELETE));
user.addPermission(Permission.EXECUTE);
System.out.println("Effective permissions: " + user.getEffectivePermissions());
}
}
5.2 状态机实现
java
enum ProcessState {
NEW, READY, RUNNING, BLOCKED, TERMINATED
}
enum Event {
START, SCHEDULE, IO_REQUEST, IO_COMPLETE, TIME_OUT, EXIT
}
class ProcessStateMachine {
private static final Map<ProcessState, EnumSet<Event>> VALID_TRANSITIONS =
Map.of(
ProcessState.NEW, EnumSet.of(Event.START),
ProcessState.READY, EnumSet.of(Event.SCHEDULE),
ProcessState.RUNNING, EnumSet.of(Event.IO_REQUEST, Event.TIME_OUT, Event.EXIT),
ProcessState.BLOCKED, EnumSet.of(Event.IO_COMPLETE),
ProcessState.TERMINATED, EnumSet.noneOf(Event.class)
);
private ProcessState currentState = ProcessState.NEW;
public boolean transition(Event event) {
EnumSet<Event> validEvents = VALID_TRANSITIONS.get(currentState);
if (validEvents.contains(event)) {
currentState = getNextState(event);
return true;
}
return false;
}
private ProcessState getNextState(Event event) {
switch (event) {
case START: return ProcessState.READY;
case SCHEDULE: return ProcessState.RUNNING;
case IO_REQUEST: return ProcessState.BLOCKED;
case IO_COMPLETE: return ProcessState.READY;
case TIME_OUT: return ProcessState.READY;
case EXIT: return ProcessState.TERMINATED;
default: throw new IllegalStateException("Invalid event");
}
}
}
5.3 配置选项管理
java
enum FeatureFlag {
NEW_UI, DARK_MODE, AUTO_SAVE, ANALYTICS,
SOCIAL_SHARE, OFFLINE_MODE, PREMIUM_FEATURES
}
class AppConfig {
private EnumSet<FeatureFlag> enabledFeatures = EnumSet.noneOf(FeatureFlag.class);
private EnumSet<FeatureFlag> experimentalFeatures = EnumSet.noneOf(FeatureFlag.class);
public void enableFeature(FeatureFlag feature) {
enabledFeatures.add(feature);
}
public void disableFeature(FeatureFlag feature) {
enabledFeatures.remove(feature);
}
public boolean isFeatureEnabled(FeatureFlag feature) {
return enabledFeatures.contains(feature);
}
public void markAsExperimental(FeatureFlag feature) {
experimentalFeatures.add(feature);
}
public EnumSet<FeatureFlag> getEnabledNonExperimentalFeatures() {
EnumSet<FeatureFlag> result = EnumSet.copyOf(enabledFeatures);
result.removeAll(experimentalFeatures);
return result;
}
public EnumSet<FeatureFlag> getFeaturesForUser(boolean isPremiumUser) {
EnumSet<FeatureFlag> features = EnumSet.copyOf(enabledFeatures);
if (!isPremiumUser) {
features.remove(FeatureFlag.PREMIUM_FEATURES);
}
return features;
}
}
6. 性能特点
6.1 时间复杂度
add(),remove(),contains(): O(1)- 集合运算:O(n),n是枚举常量数量
- 迭代:O(n)
- 空间复杂度:O(1) 到 O(n/64)
6.2 性能对比
java
// EnumSet vs HashSet vs TreeSet
enum TestEnum {
A, B, C, D, E, F, G, H, I, J,
K, L, M, N, O, P, Q, R, S, T
}
class PerformanceTest {
public static void main(String[] args) {
TestEnum[] values = TestEnum.values();
int iterations = 1000000;
// EnumSet
EnumSet<TestEnum> enumSet = EnumSet.noneOf(TestEnum.class);
long start = System.nanoTime();
for (int i = 0; i < iterations; i++) {
enumSet.add(values[i % values.length]);
enumSet.contains(values[i % values.length]);
enumSet.remove(values[i % values.length]);
}
long enumSetTime = System.nanoTime() - start;
// HashSet
Set<TestEnum> hashSet = new HashSet<>();
start = System.nanoTime();
for (int i = 0; i < iterations; i++) {
hashSet.add(values[i % values.length]);
hashSet.contains(values[i % values.length]);
hashSet.remove(values[i % values.length]);
}
long hashSetTime = System.nanoTime() - start;
System.out.println("EnumSet time: " + enumSetTime / 1_000_000 + "ms");
System.out.println("HashSet time: " + hashSetTime / 1_000_000 + "ms");
}
}
7. 与BitSet对比
| 特性 | EnumSet | BitSet |
|---|---|---|
| 元素类型 | 枚举常量 | 任意整数索引 |
| 类型安全 | 强类型,编译时检查 | 弱类型,运行时检查 |
| 性能 | 更快,专门优化 | 快,但不如EnumSet |
| 内存 | 更节省 | 节省 |
| 可读性 | 更高 | 较低 |
| 扩展性 | 固定,限于枚举常量 | 动态扩展 |
8. 使用场景和最佳实践
8.1 适用场景
- 枚举集合操作:需要存储和操作枚举值集合
- 状态管理:系统状态、权限、标志位
- 配置管理:功能开关、选项集合
- 有限状态机:状态转换
- 高性能要求:需要最佳性能的集合操作
8.2 最佳实践
java
// 1. 使用静态工厂方法代替构造函数
// 好:
EnumSet<Day> days = EnumSet.of(Day.MONDAY, Day.TUESDAY);
// 不好:
// EnumSet<Day> days = new EnumSet<>(); // 没有public构造函数
// 2. 优先使用range()创建连续范围
EnumSet<Day> workWeek = EnumSet.range(Day.MONDAY, Day.FRIDAY);
// 3. 使用noneOf()创建空集合
EnumSet<Day> empty = EnumSet.noneOf(Day.class);
// 4. 利用clone()避免修改原集合
EnumSet<Day> original = EnumSet.of(Day.MONDAY, Day.TUESDAY);
EnumSet<Day> copy = original.clone();
copy.add(Day.WEDNESDAY); // 不影响original
// 5. 与Collections工具类配合
EnumSet<Day> synchronizedSet = Collections.synchronizedSet(EnumSet.noneOf(Day.class));
EnumSet<Day> unmodifiableSet = Collections.unmodifiableSet(EnumSet.allOf(Day.class));
// 6. 合理使用complementOf()
EnumSet<Day> weekend = EnumSet.of(Day.SATURDAY, Day.SUNDAY);
EnumSet<Day> workdays = EnumSet.complementOf(weekend); // 周一至周五
8.3 线程安全处理
java
// 方案1:使用Collections.synchronizedSet
class ThreadSafeEnumSet {
private final Set<Permission> permissions =
Collections.synchronizedSet(EnumSet.noneOf(Permission.class));
public void addPermission(Permission perm) {
permissions.add(perm);
}
public boolean hasPermission(Permission perm) {
return permissions.contains(perm);
}
}
// 方案2:使用CopyOnWriteArraySet(适合读多写少)
class CopyOnWriteEnumSet {
private final Set<Permission> permissions =
new CopyOnWriteArraySet<>(EnumSet.noneOf(Permission.class));
}
// 方案3:使用锁
class LockedEnumSet {
private final Set<Permission> permissions = EnumSet.noneOf(Permission.class);
private final Object lock = new Object();
public void addPermission(Permission perm) {
synchronized(lock) {
permissions.add(perm);
}
}
}
9. 常见陷阱和注意事项
9.1 空值处理
java
// EnumSet不允许null元素
try {
EnumSet<Day> set = EnumSet.of(Day.MONDAY);
set.add(null); // 抛出NullPointerException
} catch (NullPointerException e) {
System.out.println("EnumSet不允许null元素");
}
9.2 类型安全
java
enum Color { RED, GREEN, BLUE }
enum Size { SMALL, MEDIUM, LARGE }
// 编译错误:类型不匹配
// EnumSet<Color> colors = EnumSet.of(Size.SMALL);
// 正确
EnumSet<Color> colors = EnumSet.of(Color.RED, Color.GREEN);
9.3 性能考虑
java
// 避免频繁创建
class EfficientUsage {
private static final EnumSet<Day> WEEKEND =
EnumSet.of(Day.SATURDAY, Day.SUNDAY);
// 重用常量集合
public boolean isWeekend(Day day) {
return WEEKEND.contains(day);
}
}
10. 高级用法
10.1 枚举常量分组
java
enum AppPermission {
// 用户权限
VIEW_PROFILE, EDIT_PROFILE, CHANGE_PASSWORD,
// 管理员权限
VIEW_USERS, EDIT_USERS, DELETE_USERS,
// 系统权限
BACKUP, RESTORE, CONFIG_CHANGE;
// 分组
public static final EnumSet<AppPermission> USER_PERMISSIONS =
EnumSet.of(VIEW_PROFILE, EDIT_PROFILE, CHANGE_PASSWORD);
public static final EnumSet<AppPermission> ADMIN_PERMISSIONS =
EnumSet.of(VIEW_USERS, EDIT_USERS, DELETE_USERS);
public static final EnumSet<AppPermission> SYSTEM_PERMISSIONS =
EnumSet.of(BACKUP, RESTORE, CONFIG_CHANGE);
public static final EnumSet<AppPermission> ALL_PERMISSIONS =
EnumSet.allOf(AppPermission.class);
}
10.2 枚举集合运算工具类
java
class EnumSetUtils {
// 安全的并集(不修改原集合)
public static <E extends Enum<E>> EnumSet<E> union(EnumSet<E> set1, EnumSet<E> set2) {
EnumSet<E> result = EnumSet.copyOf(set1);
result.addAll(set2);
return result;
}
// 安全的交集
public static <E extends Enum<E>> EnumSet<E> intersection(EnumSet<E> set1, EnumSet<E> set2) {
EnumSet<E> result = EnumSet.copyOf(set1);
result.retainAll(set2);
return result;
}
// 对称差
public static <E extends Enum<E>> EnumSet<E> symmetricDifference(
EnumSet<E> set1, EnumSet<E> set2) {
EnumSet<E> union = union(set1, set2);
EnumSet<E> intersection = intersection(set1, set2);
union.removeAll(intersection);
return union;
}
// 转换为位掩码
public static <E extends Enum<E>> long toBitMask(EnumSet<E> set) {
long mask = 0;
for (E element : set) {
mask |= 1L << element.ordinal();
}
return mask;
}
// 从位掩码恢复
public static <E extends Enum<E>> EnumSet<E> fromBitMask(long mask, Class<E> enumClass) {
EnumSet<E> result = EnumSet.noneOf(enumClass);
E[] constants = enumClass.getEnumConstants();
for (E constant : constants) {
if ((mask & (1L << constant.ordinal())) != 0) {
result.add(constant);
}
}
return result;
}
}
EnumSet是处理枚举集合的首选工具,它结合了类型安全、高性能和简洁的API,特别适合在需要高效处理枚举值集合的场景中使用。
