双向映射(bidimap)能天然支持“键查值”和“值查键”,一次put双向生效,避免双map不同步问题;要求键值均唯一,适用于状态码↔名称等对称关系,但别名场景需归一化处理。

双向映射(BidiMap)能天然解决“值查键”和“键查值”必须写两套逻辑的问题,在状态机中尤其适合管理状态码 ↔ 状态名、事件类型 ↔ 事件ID、阶段标识 ↔ 阶段序号等对称关系。用它替代两个独立 Map,代码更简、不易出错、内存更可控。
状态码与状态名的双向绑定
传统方式需维护 Map<integer string></integer> 和 Map<string integer></string> 两份结构,增删时易不同步。改用 BidiMap(如 Apache Commons Collections 的 TreeBidiMap 或 HashBidiMap)后,一次 put 即双向生效:
-
// 初始化状态映射
BidiMap<integer string> statusMap = new HashBidiMap();<br> statusMap.put(1, "INIT");<br> statusMap.put(2, "RUNNING");<br> statusMap.put(3, "PAUSED");<br> statusMap.put(4, "COMPLETED");</integer>
-
// 正向查:状态码 → 名称
String name = statusMap.get(2); // "RUNNING" -
// 反向查:名称 → 状态码(无需额外 Map)
Integer code = statusMap.getKey("PAUSED"); // 3
状态转换规则表的精简建模
状态机中常有“当前状态 + 事件 → 下一状态”的转换表。若用二维 Map 嵌套,查询冗长;而将 (currentState, event) 组合成复合键(如字符串 "RUNNING|STOP"),再用 BidiMap 关联目标状态,可统一索引并支持逆向追溯触发路径:
-
// 定义转换键格式:"{from}_{event}"
BidiMap<string string> transitionMap = new HashBidiMap();<br> transitionMap.put("INIT_START", "RUNNING");<br> transitionMap.put("RUNNING_STOP", "PAUSED");<br> transitionMap.put("PAUSED_RESUME", "RUNNING");</string> -
// 正向:查下一状态
String next = transitionMap.get("RUNNING_STOP"); // "PAUSED" -
// 反向:查哪些输入能到达某状态(调试/覆盖分析有用)
String trigger = transitionMap.getKey("RUNNING"); // "INIT_START" 或 "PAUSED_RESUME"(注意:BidiMap 要求值唯一,若多路径到同一状态,需改用BidiMap<string set>></string>或换方案)
避免常见陷阱
BidiMap 不是万能胶,用前需确认约束是否匹配业务:
-
值必须唯一:BidiMap 要求 value 也具备 key 的唯一性。若多个状态码对应同一名称(如别名场景),不能直接用原生 BidiMap,应先归一化或改用
MultiValueMap+ 手动反查 -
线程安全需显式保障:Apache Commons 的 BidiMap 默认非线程安全。高并发状态更新场景下,需包装为
Collections.synchronizedMap()或选用ConcurrentHashMap+ 自定义双向缓存 -
序列化兼容性:部分 BidiMap 实现(如 TreeBidiMap)依赖比较器,反序列化时若类路径或 comparator 不一致会失败。生产环境建议优先选
HashBidiMap并显式测试序列化流程
现代 Java 替代方案参考
若项目已升级至 Java 16+ 且不愿引入 Commons Collections,可用 Map + 封装类模拟轻量双向行为:
-
// 简单封装,保证 put/remove 原子性
public class SimpleBidiMap<k> {<br> private final Map<k v> forward = new HashMap();<br> private final Map<v k> backward = new HashMap();<br> public void put(K k, V v) {<br> forward.put(k, v);<br> backward.put(v, k);<br> }<br> public V get(K k) { return forward.get(k); }<br> public K getKey(V v) { return backward.get(v); }<br> }</v></k></k> - 优势:无第三方依赖、可控性强;缺点:不支持排序、无内置视图(如 inverseBidiMap)











