0Pricing
Java Academy · 课时

EnumSet

对枚举执行快速集合操作

EnumSet 是 CoddyKit 上的免费 Java Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。

什么是 EnumSet

EnumSet 是一种仅适用于枚举类型的专用 Set 实现。

它的内部结构是一个位向量:每个常量对应一位。与存放枚举的 HashSet 相比,这使它的速度极快且非常节省内存。

import java.util.EnumSet;
import java.util.Set;

public class Main {
    enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
    public static void main(String[] args) {
        Set<Day> weekend = EnumSet.of(Day.SAT, Day.SUN);
        System.out.println(weekend);
    }
}

使用 of 创建集合

工厂方法 EnumSet.of(...) 根据列出的常量创建集合。它为一个到五个元素分别提供了重载版本,此外还提供可变参数版本。

import java.util.EnumSet;

public class Main {
    enum Perm { READ, WRITE, EXECUTE }
    public static void main(String[] args) {
        EnumSet<Perm> perms = EnumSet.of(Perm.READ, Perm.WRITE);
        System.out.println(perms);
    }
}

allOf 与 noneOf

还有两个工厂方法:

  • allOf(Type.class) 创建一个包含所有常量的集合。
  • noneOf(Type.class) 创建一个空集合,供后续填充。
import java.util.EnumSet;

public class Main {
    enum Color { RED, GREEN, BLUE }
    public static void main(String[] args) {
        EnumSet<Color> all = EnumSet.allOf(Color.class);
        EnumSet<Color> none = EnumSet.noneOf(Color.class);
        none.add(Color.RED);
        System.out.println("all: " + all);
        System.out.println("built: " + none);
    }
}

连续常量的 range

EnumSet.range(from, to) 按声明顺序包含两个端点之间的所有常量,包括这两个端点。

import java.util.EnumSet;

public class Main {
    enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
    public static void main(String[] args) {
        EnumSet<Day> workdays = EnumSet.range(Day.MON, Day.FRI);
        System.out.println(workdays);
    }
}

complementOf

EnumSet.complementOf(set) 返回给定集合中不包含的所有常量组成的集合。

这是快速反转选择结果的方法。

import java.util.EnumSet;

public class Main {
    enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
    public static void main(String[] args) {
        EnumSet<Day> work = EnumSet.range(Day.MON, Day.FRI);
        EnumSet<Day> rest = EnumSet.complementOf(work);
        System.out.println(rest);
    }
}

集合运算

由于 EnumSet 是真正的集合,您可以通过 addAll、retainAll 和 removeAll 获得并集、交集和差集。

这些操作在底层以位运算的速度执行。

import java.util.EnumSet;

public class Main {
    enum Tag { A, B, C, D }
    public static void main(String[] args) {
        EnumSet<Tag> x = EnumSet.of(Tag.A, Tag.B, Tag.C);
        EnumSet<Tag> y = EnumSet.of(Tag.B, Tag.C, Tag.D);
        EnumSet<Tag> inter = EnumSet.copyOf(x);
        inter.retainAll(y);
        System.out.println("intersection: " + inter);
    }
}

成员测试

contains 只需进行一次位检查,因此是常数时间操作,并且速度非常快。

import java.util.EnumSet;

public class Main {
    enum Feature { DARK_MODE, BETA, ANALYTICS }
    public static void main(String[] args) {
        EnumSet<Feature> enabled = EnumSet.of(Feature.DARK_MODE, Feature.BETA);
        System.out.println(enabled.contains(Feature.BETA));
        System.out.println(enabled.contains(Feature.ANALYTICS));
    }
}

用 EnumSet 替代标志位

在 EnumSet 出现以前,程序员会使用类似 int bit flags 的整型位标志。EnumSet 以类型安全和可读性取代了这种做法。

您可以获得位字段的性能,同时避免容易出错的手动掩码操作。

import java.util.EnumSet;
import java.util.Set;

public class Main {
    enum Style { BOLD, ITALIC, UNDERLINE }
    static void render(Set<Style> styles) {
        System.out.println("Applying: " + styles);
    }
    public static void main(String[] args) {
        render(EnumSet.of(Style.BOLD, Style.UNDERLINE));
    }
}

按声明顺序迭代

EnumSet 按常量的自然顺序迭代,也就是它们的声明顺序。

与 HashSet 不同,这会带来可预测且稳定的迭代顺序。

import java.util.EnumSet;

public class Main {
    enum Phase { ALPHA, BETA, RC, GA }
    public static void main(String[] args) {
        EnumSet<Phase> set = EnumSet.of(Phase.GA, Phase.ALPHA, Phase.RC);
        for (Phase p : set) System.out.println(p);
    }
}

使用 copyOf 创建防御性副本

EnumSet.copyOf(collection) 可以从任意枚举值集合构建 EnumSet。您可以用它创建安全副本,或将列表转换为 EnumSet。

import java.util.EnumSet;
import java.util.List;

public class Main {
    enum Role { ADMIN, EDITOR, VIEWER }
    public static void main(String[] args) {
        List<Role> source = List.of(Role.EDITOR, Role.VIEWER);
        EnumSet<Role> roles = EnumSet.copyOf(source);
        System.out.println(roles);
    }
}

为何速度很快

对于包含 64 个或更少常量的枚举,EnumSet 使用单个 long 作为位向量。每次操作只需一到两条机器指令。

对于枚举集合,请始终优先使用 EnumSet,而不是 HashSet<MyEnum>。

import java.util.EnumSet;

public class Main {
    enum Bit { B0, B1, B2, B3 }
    public static void main(String[] args) {
        EnumSet<Bit> s = EnumSet.of(Bit.B0, Bit.B2);
        // contains is a single bit-and under the hood
        System.out.println(s.contains(Bit.B2));
    }
}

快速检查

检验您对 EnumSet 的理解。

回顾

您已经学会了 EnumSet:

  • 一种用于枚举的位向量 Set,速度非常快且十分紧凑。
  • 工厂方法包括:of、allOf、noneOf、range、complementOf 和 copyOf。
  • 按声明顺序迭代。
  • 类型安全的整型位标志替代方案。

接下来将学习对应的映射类型:EnumMap。

import java.util.EnumSet;

public class Main {
    enum X { A, B }
    public static void main(String[] args) {
        System.out.println("EnumSet recap: " + EnumSet.allOf(X.class));
    }
}

常见问题解答

「EnumSet」课时是免费的吗?

是的 — 「EnumSet」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。

「EnumSet」这节课中我会学到什么?

对枚举执行快速集合操作 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Java Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「EnumSet」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Java Academy 课中编写并运行代码吗?

能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 带字段和方法的枚举
  2. 枚举中的抽象方法
  3. EnumSet
  4. EnumMap
← 返回 Java Academy