0Pricing
Java Academy · 课时

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()); // 7

EnumSet:集合运算

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: $581450

EnumMap:按顺序迭代

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));     // true

EnumMap 的 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 定义和使用枚举
  2. 带字段和方法的枚举
  3. 使用枚举的 switch 表达式
  4. EnumSet 与 EnumMap
← 返回 Java Academy