enummap 用枚举 ordinal 直接作数组下标实现 o(1) 存取,不依赖哈希、无冲突处理、无装箱开销、内存紧凑;构造时需传入枚举类字面量,键类型严格限定,遍历顺序即声明顺序。

EnumMap 用数组高效存储枚举键,核心在于利用枚举类的天然特性:每个枚举常量在类加载时就确定了唯一的、从 0 开始连续递增的 ordinal 值。 它不使用哈希表,也不泛型擦除后退化为 Object 数组,而是直接以枚举的 ordinal 作为数组下标,实现 O(1) 的存取,且无哈希冲突、无装箱开销、内存紧凑。
为什么能用数组?——依赖枚举的 ordinal 连续性
Java 枚举类继承自 java.lang.Enum,每个实例在编译期就被分配一个不可变的 ordinal(声明顺序索引)。例如:
enum Color { RED, GREEN, BLUE }
// RED.ordinal() == 0, GREEN.ordinal() == 1, BLUE.ordinal() == 2
EnumMap 在构造时通过 keyType.getEnumConstants() 获取所有枚举常量,得到长度 n,随即创建长度为 n 的 Object[] table。键 k 的存储位置就是 k.ordinal(),无需计算 hash,也无需处理冲突。
数组怎么存?——table[i] 直接对应第 i 个枚举项
内部数组 table 是纯数据容器,索引与枚举序号严格对齐:
- 若
map.put(Color.RED, "r"),则写入table[0] = "r" - 若
map.put(Color.BLUE, "b"),则写入table[2] = "b" - 未 put 的位置(如
table[1])保持为null,表示该枚举键无映射
这种设计避免了 HashMap 中的 Node 链表/红黑树结构,也没有 LinkedHashMap 的双向链表开销,空间利用率接近 100%(仅存值对象引用 + 少量字段)。
如何保证类型安全?——编译期 + 运行期双重约束
EnumMap 构造时必须传入枚举类字节码(Class<k></k>),它会检查:
- 编译期:泛型参数
K extends Enum<k></k>强制限定键只能是枚举 - 运行期:调用
key.getClass() != keyType会抛ClassCastException,防止子类枚举或伪造 key - 所有
put(K key, V value)操作都先校验key.getDeclaringClass() == keyType
因此,数组下标访问前已确保 key.ordinal() 落在 [0, table.length) 范围内,不会越界,也无需额外边界检查。
和 HashMap 比有什么优势?
在键确定为枚举的场景下:
- 更快:省去 hash 计算、equals 比较、寻址跳转;get/put 平均耗时低 3–5 倍
- 更省:无 Node 对象头、无 next 引用、无负载因子预留空间;内存占用约为 HashMap 的 40%
- 更稳:无扩容机制(数组大小固定)、无并发问题(非线程安全但行为可预测)、无哈希扰动风险
适合配置映射、状态码翻译、协议字段缓存等枚举键明确且数量稳定的应用场景。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











