1. 概述

本文将深入探讨 Apache Commons Collections 库中的一个有趣数据结构——BidiMap(双向映射)。它扩展了标准 Map 接口,新增了通过值反向查找键的能力,这种特性在特定场景下非常实用。

2. 依赖配置

使用 BidiMap 前需添加以下依赖。Maven 项目pom.xml 中添加:

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.1</version>
</dependency>

Gradle 项目build.gradle 中添加:

compile 'org.apache.commons:commons-collections4:4.1'

⚠️ 最新版本请查阅 Maven Central

3. 实现类与实例化

BidiMap 仅定义接口规范,具体实现需关注以下类。核心原则:所有实现类均不允许键或值重复(反转映射时值会变成键,违反 Map 唯一键约束)。

3.1 主流实现对比

实现类 底层结构 特点 适用场景
DualHashBidiMap HashMap ✅ O(1) 级别的键/值查询
❌ 需维护两个 Map 实例
高频查询且不关心顺序的场景
DualLinkedHashBidiMap LinkedHashMap ✅ 保留插入顺序
❌ 性能开销略高于 DualHashBidiMap
需要遍历顺序的场景
TreeBidiMap 红黑树 ✅ 键/值自动排序
✅ 内存占用低
❌ 查询/插入为 O(log n)
需要有序遍历的场景
DualTreeBidiMap TreeMap ✅ 功能同 TreeBidiMap
❌ 性能开销更大(维护两个树结构)
不推荐(直接用 TreeBidiMap

3.2 实例化方式

所有实现类均可直接替换标准 Map,简单粗暴地无参构造即可:

BidiMap<String, String> map = new DualHashBidiMap<>();  // 最常用
BidiMap<String, String> orderedMap = new DualLinkedHashBidiMap<>();  // 需要顺序时

4. 核心方法详解

BidiMap 在标准 Map 之上提供了四个关键方法,解决开发中的常见痛点。

4.1 put() - 智能插入机制

插入键值对时,若值已存在则自动覆盖旧条目,避免重复值导致的冲突:

BidiMap<String, String> map = new DualHashBidiMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
assertEquals(map.size(), 2);  // ✅ 成功插入

// 踩坑示例:插入重复值
map.put("key3", "value1");  // 自动移除 key1-value1
assertEquals(map.size(), 2);  // ❌ 实际只有 key2-value2 和 key3-value1

4.2 inverseBidiMap() - 反转映射

一键生成键值互换的新 BidiMap,翻译/字典场景的利器

BidiMap<String, String> map = new DualHashBidiMap<>();
map.put("China", "中国");
map.put("Japan", "日本");

BidiMap<String, String> reversedMap = map.inverseBidiMap();
assertTrue(reversedMap.containsKey("中国"));  // ✅ 反向查询成功
assertEquals(reversedMap.get("日本"), "Japan");

4.3 removeValue() - 按值删除

突破标准 Map 限制,直接通过值删除条目:

BidiMap<String, String> map = new DualHashBidiMap<>();
map.put("user1", "张三");
map.put("user2", "李四");

map.removeValue("张三");  // 直接删除值对应的条目
assertFalse(map.containsKey("user1"));  // ✅ user1 已被移除

4.4 getKey() - 值反向查键

快速获取值对应的键,避免手动遍历:

BidiMap<String, String> map = new DualHashBidiMap<>();
map.put("id001", "admin");

String key = map.getKey("admin");
assertEquals(key, "id001");  // ✅ 直接获取键
assertNull(map.getKey("nonexistent"));  // ❌ 不存在则返回 null

5. 总结

BidiMap 的核心价值在于双向查询能力,特别适合:

  • 翻译词典(中英互查)
  • ID 映射(如用户名与 ID 互查)
  • 配置管理(键值需双向访问的场景)

💡 实战建议:优先使用 DualHashBidiMap 满足大多数需求,仅在需要顺序时选择 DualLinkedHashBidiMap,排序场景用 TreeBidiMap

完整代码示例见 GitHub 仓库


原始标题:Apache Commons Collections BidiMap