0Pricing
Java Academy · 课时

EnumMap

高效的枚举键映射

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

什么是 EnumMap

EnumMap 是一种专用 Map,其键是同一种枚举类型的常量。

它的内部结构由一个普通数组支持,数组索引是常量的 ordinal。这使查找和插入极其快速,而且无需进行哈希计算。

import java.util.EnumMap;
import java.util.Map;

public class Main {
    enum Day { MON, TUE, WED }
    public static void main(String[] args) {
        Map<Day, String> plan = new EnumMap<>(Day.class);
        plan.put(Day.MON, "Gym");
        plan.put(Day.WED, "Swim");
        System.out.println(plan);
    }
}

构造时需要类对象

EnumMap 必须预先知道键的类型,因此构造函数接收类对象:new EnumMap<>(Day.class)。

这样它就能根据常量数量确定内部数组的大小。

import java.util.EnumMap;

public class Main {
    enum Priority { LOW, MEDIUM, HIGH }
    public static void main(String[] args) {
        EnumMap<Priority, Integer> counts = new EnumMap<>(Priority.class);
        counts.put(Priority.HIGH, 5);
        System.out.println(counts.get(Priority.HIGH));
    }
}

数组支持的高性能

由于键存储在 array[ordinal] 中,get 和 put 都是直接的数组访问,比 HashMap 的哈希计算和桶遍历更快。

键不会发生装箱,也不会调用 hashCode。

import java.util.EnumMap;

public class Main {
    enum Slot { A, B, C, D }
    public static void main(String[] args) {
        EnumMap<Slot, Integer> m = new EnumMap<>(Slot.class);
        for (Slot s : Slot.values()) m.put(s, s.ordinal() * 10);
        System.out.println(m.get(Slot.C));
    }
}

按键顺序迭代

EnumMap 按键的自然顺序迭代条目,也就是键的声明顺序。

这会带来稳定且可预测的输出,不同于 HashMap。

import java.util.EnumMap;
import java.util.Map;

public class Main {
    enum Phase { PLAN, BUILD, SHIP }
    public static void main(String[] args) {
        Map<Phase, Integer> hours = new EnumMap<>(Phase.class);
        hours.put(Phase.SHIP, 3);
        hours.put(Phase.PLAN, 8);
        hours.put(Phase.BUILD, 20);
        for (Map.Entry<Phase, Integer> e : hours.entrySet()) {
            System.out.println(e.getKey() + " = " + e.getValue());
        }
    }
}

禁止空键

EnumMap不允许空键;空键会抛出 NullPointerException。

空值是允许的,与其他映射一样。

import java.util.EnumMap;

public class Main {
    enum K { A, B }
    public static void main(String[] args) {
        EnumMap<K, String> m = new EnumMap<>(K.class);
        m.put(K.A, null); // null value is fine
        System.out.println("A -> " + m.get(K.A));
        System.out.println("contains A: " + m.containsKey(K.A));
    }
}

使用 EnumMap 分组

一种常见用途是按枚举类别进行计数或分组。merge 可以简洁地完成计数。

import java.util.EnumMap;
import java.util.List;
import java.util.Map;

public class Main {
    enum Type { FRUIT, VEG, MEAT }
    record Item(String name, Type type) {}
    public static void main(String[] args) {
        List<Item> items = List.of(new Item("apple", Type.FRUIT), new Item("pear", Type.FRUIT), new Item("beef", Type.MEAT));
        Map<Type, Integer> counts = new EnumMap<>(Type.class);
        for (Item i : items) counts.merge(i.type(), 1, Integer::sum);
        System.out.println(counts);
    }
}

getOrDefault 与 computeIfAbsent

Map 的所有标准默认方法都可以使用。getOrDefault 可以避免空值检查,而 computeIfAbsent 会延迟构建值。

import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;

public class Main {
    enum Group { A, B }
    public static void main(String[] args) {
        Map<Group, List<String>> m = new EnumMap<>(Group.class);
        m.computeIfAbsent(Group.A, k -> new ArrayList<>()).add("first");
        System.out.println(m);
        System.out.println(m.getOrDefault(Group.B, List.of()));
    }
}

复制构造函数

EnumMap 提供一个接收另一个 Map 的复制构造函数。如果源对象是 EnumMap,则会从中推断键类型。

import java.util.EnumMap;
import java.util.Map;

public class Main {
    enum K { X, Y }
    public static void main(String[] args) {
        EnumMap<K, Integer> a = new EnumMap<>(K.class);
        a.put(K.X, 1);
        EnumMap<K, Integer> b = new EnumMap<>(a);
        b.put(K.Y, 2);
        System.out.println("copy: " + b);
    }
}

EnumMap 与 ordinal 数组的比较

在 EnumMap 出现以前,人们会使用以 ordinal() 为索引的原始数组。这种方式脆弱且不安全。

EnumMap 在提供相同数组速度的同时,还提供类型安全、边界安全以及清晰的 Map 接口。

import java.util.EnumMap;
import java.util.Map;

public class Main {
    enum Season { SPRING, SUMMER, FALL, WINTER }
    public static void main(String[] args) {
        Map<Season, String> mood = new EnumMap<>(Season.class);
        mood.put(Season.SUMMER, "sunny");
        // Safer and clearer than String[] indexed by ordinal
        System.out.println(mood.get(Season.SUMMER));
    }
}

嵌套 EnumMap

您可以嵌套 EnumMap,用两个枚举作为键来表示矩阵,例如状态转换表。

import java.util.EnumMap;
import java.util.Map;

public class Main {
    enum State { SOLID, LIQUID, GAS }
    enum Transition { MELT, FREEZE }
    public static void main(String[] args) {
        Map<State, Map<Transition, State>> table = new EnumMap<>(State.class);
        Map<Transition, State> solid = new EnumMap<>(Transition.class);
        solid.put(Transition.MELT, State.LIQUID);
        table.put(State.SOLID, solid);
        System.out.println(table.get(State.SOLID).get(Transition.MELT));
    }
}

选择合适的 Type

经验法则:

  • 如果键是枚举常量,并且您需要映射,请使用 EnumMap。
  • 如果您需要枚举集合,请使用 EnumSet。
  • 如果键是任意对象,请使用 HashMap。
import java.util.EnumMap;
import java.util.Map;

public class Main {
    enum Level { DEBUG, INFO, WARN, ERROR }
    public static void main(String[] args) {
        Map<Level, Integer> thresholds = new EnumMap<>(Level.class);
        for (Level l : Level.values()) thresholds.put(l, l.ordinal() * 100);
        System.out.println(thresholds);
    }
}

快速检查

检验您对 EnumMap 的理解。

回顾

您已经学会了 EnumMap:

  • 一种以枚举常量为键、由数组支持的 Map。
  • 构造函数接收键的类对象;不允许使用空键。
  • 按声明顺序迭代。
  • 一种安全且快速的 ordinal 索引数组替代方案。

您已经完成高级枚举课程。

import java.util.EnumMap;

public class Main {
    enum K { A }
    public static void main(String[] args) {
        EnumMap<K, String> m = new EnumMap<>(K.class);
        m.put(K.A, "done");
        System.out.println("EnumMap course complete: " + m.get(K.A));
    }
}

常见问题解答

「EnumMap」课时是免费的吗?

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

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

高效的枚举键映射 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Java Academy 需要有经验吗?

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

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

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

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

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

此课程中的所有课时

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