EnumMap 查找是“数组级”的,因其内部用 Object[] 数组按枚举 ordinal() 直接索引,无哈希计算、无冲突处理、无扩容、无装箱,时间复杂度稳定 O(1) 且常数极小。

用 EnumMap 替代 HashMap 存储枚举类型键,本质是把哈希查找降维成数组下标访问——因为枚举的 ordinal() 天然连续、从 0 开始,EnumMap 内部直接用数组存储,没有哈希计算、无冲突处理、不扩容、无装箱开销。
为什么 EnumMap 查找是“数组级”的
EnumMap 不是哈希表,它内部维护一个 Object[] table,长度等于该枚举类的常量总数(即 MyEnum.values().length)。每个枚举实例的存储位置就是它的 ordinal() 值:
– put(E key, V value) → 直接写入 table[key.ordinal()]
– get(Object key) → 若 key 是目标枚举类型,直接返回 table[((Enum)key).ordinal()]
没有 hash code 计算,没有链表/红黑树遍历,没有类型检查开销(泛型擦除后仍是数组访问),时间复杂度稳定 O(1),且常数极小。
使用前提与强制约束
- 键类型必须是单一、具体、已知的枚举类(如
Color),不能是Enum<?>或其父类 - 所有键必须来自同一枚举类(
EnumMap<Color, String>里不能混入Size枚举实例) - 枚举常量顺序一旦发布就不能随意调整(虽然不影响正确性,但会改变底层数组索引映射,影响序列化兼容性)
典型替换写法与性能对比
原来这样写:
Map<Status, Integer> statusCount = new HashMap<>(); statusCount.put(Status.ACTIVE, 100); statusCount.put(Status.INACTIVE, 25); // get 走哈希:计算 Status.ACTIVE.hashCode() → 找桶 → 可能遍历节点 int activeCnt = statusCount.get(Status.ACTIVE);
换成 EnumMap:
Map<Status, Integer> statusCount = new EnumMap<>(Status.class); statusCount.put(Status.ACTIVE, 100); statusCount.put(Status.INACTIVE, 25); // get 直接:table[Status.ACTIVE.ordinal()],一次内存读取
实测在热点路径中,get() 吞吐量可提升 2~4 倍,GC 压力显著降低(无 Entry 对象分配)。
注意事项与避坑点
- 不要用 EnumMap 存 null 键:编译期就报错(构造时传入的 Class 不能为空,且 put 会校验键是否为该枚举实例)
- null 值允许存,但语义需明确(和 HashMap 一样,get 返回 null 可能表示不存在或存了 null)
- 遍历时顺序按枚举声明顺序(
values()顺序),不是插入顺序,也不是哈希顺序——这是优势也是约束 - 如果需要支持多种枚举类型统一处理,别强行泛型抽象,应为每种枚举单独声明
EnumMap

















