EnumSet 与 EnumMap
使用 EnumSet 和 EnumMap 高效且类型安全地处理基于枚举的集合
EnumSet 与 EnumMap 是 CoddyKit 上的免费 Java Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
EnumSet 与 EnumMap
EnumSet 和 EnumMap 是针对枚举高度优化的集合实现。它们在内部使用位向量,对于枚举键来说,比 HashSet/HashMap 快得多。
EnumSet:创建集合
EnumSet 提供了用于创建枚举常量集合的工厂方法。
import java.util.*;
enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
EnumSet<Day> weekdays = EnumSet.range(Day.MON, Day.FRI);
EnumSet<Day> weekend = EnumSet.of(Day.SAT, Day.SUN);
EnumSet<Day> allDays = EnumSet.allOf(Day.class);
EnumSet<Day> noDays = EnumSet.noneOf(Day.class);
System.out.println(weekdays); // [MON, TUE, WED, THU, FRI]
System.out.println(weekend); // [SAT, SUN]
System.out.println(allDays.size()); // 7EnumSet:集合运算
EnumSet 支持标准集合运算,包括并集、交集和补集。由于采用位向量实现,这些运算的速度非常快。
import java.util.*;
enum Permission { READ, WRITE, DELETE, ADMIN, EXPORT }
EnumSet<Permission> userPerms = EnumSet.of(Permission.READ, Permission.WRITE);
EnumSet<Permission> adminPerms = EnumSet.allOf(Permission.class);
// Complement
EnumSet<Permission> missing = EnumSet.complementOf(userPerms);
System.out.println("Missing: " + missing);
// Missing: [DELETE, ADMIN, EXPORT]
// Intersection (retain)
EnumSet<Permission> shared = EnumSet.copyOf(userPerms);
shared.retainAll(adminPerms);
System.out.println("Shared: " + shared); // [READ, WRITE]EnumMap:将枚举映射到值
EnumMap 在内部使用枚举序号作为数组索引,其查找复杂度为 O(1),速度快于 HashMap。
import java.util.*;
enum Quarter { Q1, Q2, Q3, Q4 }
EnumMap<Quarter, Double> revenue = new EnumMap<>(Quarter.class);
revenue.put(Quarter.Q1, 125_000.0);
revenue.put(Quarter.Q2, 148_500.0);
revenue.put(Quarter.Q3, 132_750.0);
revenue.put(Quarter.Q4, 175_200.0);
double total = revenue.values().stream()
.mapToDouble(Double::doubleValue).sum();
System.out.printf("Annual revenue: $%.0f%n", total);
// Annual revenue: $581450EnumMap:按顺序迭代
EnumMap 始终按照枚举声明顺序迭代键,而不是按照插入顺序或哈希顺序。
import java.util.*;
enum Month { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC }
EnumMap<Month, Integer> salesUnits = new EnumMap<>(Month.class);
salesUnits.put(Month.MAR, 1420);
salesUnits.put(Month.JAN, 980);
salesUnits.put(Month.FEB, 1105);
// Iterates JAN, FEB, MAR (declaration order, not insertion order)
for (var entry : salesUnits.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// JAN: 980
// FEB: 1105
// MAR: 1420使用 EnumSet 实现权限系统
使用 EnumSet 实现基于角色的权限系统,以高效检查权限并提供清晰的 API 设计。
import java.util.*;
enum Permission { READ, WRITE, DELETE, PUBLISH, ADMIN }
record Role(String name, EnumSet<Permission> permissions) {
boolean can(Permission p) { return permissions.contains(p); }
}
Role editor = new Role("Editor",
EnumSet.of(Permission.READ, Permission.WRITE, Permission.PUBLISH));
Role viewer = new Role("Viewer",
EnumSet.of(Permission.READ));
System.out.println(editor.can(Permission.WRITE)); // true
System.out.println(viewer.can(Permission.DELETE)); // false使用 EnumMap 实现功能开关
EnumMap 非常适合功能开关系统:以枚举作为键,能够进行快速的 O(1) 查找。
import java.util.*;
enum Feature { DARK_MODE, BETA_EDITOR, AI_SUGGESTIONS, EXPORT_PDF }
class FeatureFlags {
private final EnumMap<Feature, Boolean> flags = new EnumMap<>(Feature.class);
public FeatureFlags() {
for (Feature f : Feature.values()) flags.put(f, false);
}
public void enable(Feature f) { flags.put(f, true); }
public void disable(Feature f) { flags.put(f, false); }
public boolean isEnabled(Feature f) { return flags.getOrDefault(f, false); }
}
FeatureFlags ff = new FeatureFlags();
ff.enable(Feature.DARK_MODE);
System.out.println(ff.isEnabled(Feature.DARK_MODE)); // true
System.out.println(ff.isEnabled(Feature.AI_SUGGESTIONS)); // false性能:EnumSet 与 HashSet
对于包含不超过 64 个常量的枚举,EnumSet 使用单个 long 位掩码,运算都是 O(1) 位运算。HashSet 使用哈希桶,速度更慢且额外开销更大。
import java.util.*;
enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
// EnumSet: bit operations on a long value
EnumSet<Day> workDays = EnumSet.range(Day.MON, Day.FRI);
// HashSet: hash table with boxing overhead
Set<Day> hashWorkDays = new HashSet<>(Arrays.asList(
Day.MON, Day.TUE, Day.WED, Day.THU, Day.FRI));
// Both work, but EnumSet is ~5x faster for contains/add/remove
// and uses less memory
System.out.println(workDays.contains(Day.SAT)); // false
System.out.println(workDays.contains(Day.MON)); // trueEnumMap 的 getOrDefault 与 computeIfAbsent
EnumMap 支持所有 Map 操作,包括 getOrDefault、computeIfAbsent 和合并操作。
import java.util.*;
enum Category { FOOD, ELECTRONICS, CLOTHING, BOOKS }
EnumMap<Category, List<String>> catalog = new EnumMap<>(Category.class);
// computeIfAbsent creates the list on first use
catalog.computeIfAbsent(Category.BOOKS, k -> new ArrayList<>()).add("Clean Code");
catalog.computeIfAbsent(Category.BOOKS, k -> new ArrayList<>()).add("Effective Java");
catalog.computeIfAbsent(Category.ELECTRONICS, k -> new ArrayList<>()).add("Laptop");
System.out.println(catalog.get(Category.BOOKS));
// [Clean Code, Effective Java]
System.out.println(catalog.getOrDefault(Category.FOOD, List.of()));
// []使用 EnumMap 实现多重映射模式
将 EnumMap 与 List 值结合起来,可以创建以枚举为键的多重映射,从而高效地按类别对项目进行分组。
import java.util.*;
import java.util.stream.*;
enum Priority { LOW, NORMAL, HIGH, URGENT }
record Task(String name, Priority priority) {}
List<Task> tasks = List.of(
new Task("Fix login bug", Priority.URGENT),
new Task("Update docs", Priority.LOW),
new Task("Add unit tests",Priority.NORMAL),
new Task("Deploy hotfix", Priority.URGENT)
);
// Group by priority into EnumMap
EnumMap<Priority, List<Task>> byPriority = tasks.stream()
.collect(Collectors.groupingBy(Task::priority,
() -> new EnumMap<>(Priority.class),
Collectors.toList()));
byPriority.forEach((p, ts) ->
System.out.println(p + ": " + ts.stream().map(Task::name).collect(Collectors.joining(", "))));何时使用 EnumSet 与 EnumMap
选择哪一种集合的总结:
- EnumSet:需要一组枚举值时使用,例如权限、启用的功能和选中的日期
- EnumMap:需要将每个枚举常量映射到一个值时使用,例如每个功能的配置、每个等级的分数和每个类别的项目
- 两者:对于枚举键,优先于 HashSet/HashMap 使用——速度更快、占用内存更少,并且能够保持顺序
快速检查
对于包含 64 个或更少常量的枚举,EnumSet 使用的内部数据结构是什么?
回顾:EnumSet 与 EnumMap
要点:
- EnumSet 使用位向量,对于较小的枚举集合来说速度极快
- EnumSet.of()、range()、allOf() 和 noneOf() 是常用的工厂方法
- EnumMap 使用按序号索引的数组,查找复杂度为 O(1),并按声明顺序排列
- 使用 EnumSet 表示权限集合、功能开关以及按星期几筛选
- 使用 EnumMap 表示按类别配置、评分表以及从功能映射到值的关系
- 两者都保持枚举声明顺序,比基于哈希的集合更加高效
常见问题解答
「EnumSet 与 EnumMap」课时是免费的吗?
是的 — 「EnumSet 与 EnumMap」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「EnumSet 与 EnumMap」这节课中我会学到什么?
使用 EnumSet 和 EnumMap 高效且类型安全地处理基于枚举的集合 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「EnumSet 与 EnumMap」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 定义和使用枚举
- 带字段和方法的枚举
- 使用枚举的 switch 表达式
- EnumSet 与 EnumMap