Guava中的增强Map - Table、BiMap、Multimap、RangeMap、ClassToInstanceMap

1. 简介#

  日常开发中使用Map时经常会遇到很多复杂的处理场景,例如:多个键的Map、不仅可以根据键获取值也可以根据值获取键且不用遍历、重复键的Map、数字等范围内映射相同的值、内存中缓存对象等,Guava提供了以上场景的解决方案。

场景 解决方案 具体实现
多个键的Map Table HashBasedTable、TreeBasedTable、ImmutableTable
不仅可以根据键获取值也可以根据值获取键且不用遍历 BiMap HashBiMap、ImmutableBiMap
重复键的Map Multimap ArrayListMultimap、LinkedListMultimap、LinkedHashMultimap、ImmutableListMultimap、ImmutableSetMultimap
数字等范围内映射相同的值 RangeMap TreeRangeMap、ImmutableRangeMap
内存中缓存对象 ClassToInstanceMap MutableClassToInstanceMap、ImmutableClassToInstanceMap

  本博客将详细描述具体的示例代码。

2. 添加依赖#

  Maven项目pom.xml中添加依赖:

Copy
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>32.0.0-jre</version> </dependency>

3. Tbale - 表结构数据#

  官方注释翻译:将一对有序键(称为行键和列键)与单个值相关联的集合。
  示例代码(需求: 记录各个公司每个部门的人数):

Copy
// HashMap Map<String, Integer> deptMap = new HashMap<>(); deptMap.put("A部门", 10); deptMap.put("B部门", 20); Map<String, Map<String, Integer>> companyMap = new HashMap<>(); companyMap.put("xx公司", deptMap); // HashMap 获取值 Integer val = companyMap.get("xx公司").get("A部门"); System.out.println("HashMap 获取值: " + val); // 创建Hash类型Table, 基于Hash表实现 // Table<R, C, V>中三个泛型: R-行, C-列, V-值 Table<String, String, Integer> hashTable = HashBasedTable.create(); hashTable.put("xx公司", "A部门", 10); hashTable.put("xx公司", "B部门", 20); hashTable.put("xx公司", "C部门", 30); System.out.println("\nHash Table: " + hashTable); // 创建Tree类型Table, 基于红黑树实现 Table<String, String, Integer> treeTable = TreeBasedTable.create(); treeTable.put("xx公司", "C部门", 30); treeTable.put("xx公司", "B部门", 20); treeTable.put("xx公司", "A部门", 10); System.out.println("\nTree Table: " + treeTable); // 创建不可变Table, 无法新增、更新或删除 Table<String, String, Integer> immutableTable = ImmutableTable.<String, String, Integer>builder() .put("xx公司", "C部门", 30) .put("xx公司", "B部门", 20) .put("xx公司", "A部门", 10) .build(); System.out.println("\nImmutable Table: " + immutableTable); // Table 获取值 Integer val2 = hashTable.get("xx公司", "A部门"); System.out.println("\nTable 获取值: " + val2); // Table 删除值 Integer remove = hashTable.remove("xx公司", "C部门"); System.out.println("\nTable 删除值: " + remove); // 根据行获取列和值映射 Map<String, Integer> columnvalueMap = hashTable.row("xx公司"); System.out.println("\nTable 列和值 映射: " + columnvalueMap); // 根据列获取行和值映射 Map<String, Integer> rowvalueMap = hashTable.column("A部门"); System.out.println("\nTable 行和值 映射: " + rowvalueMap); // 获取key集合 Set<String> rowKeySet = hashTable.rowKeySet(); System.out.println("\nTable Row key 集合: " + rowKeySet); Set<String> columnKeySet = hashTable.columnKeySet(); System.out.println("\nTable Column key 集合: " + columnKeySet); // 获取值集合 Collection<Integer> values = hashTable.values(); System.out.println("\nTable 值集合: " + values); // 判断包含行 boolean containsRow = hashTable.containsRow("xx公司"); System.out.println("\nTable 包含行: " + containsRow); // 判断包含列 boolean containsColumn = hashTable.containsColumn("A部门"); System.out.println("\nTable 包含列: " + containsColumn); // 判断包含行和列 boolean contains = hashTable.contains("xx公司", "A部门"); System.out.println("\nTable 包含行和列: " + contains); // 判断包含值 boolean containsValue = hashTable.containsValue(10); System.out.println("\nTable 包含值: " + containsValue); // 行和列转置 - 行 转 列 Table<String, String, Integer> transposeTable = Tables.transpose(hashTable); // 获取所有的行 Set<Table.Cell<String, String, Integer>> cells = transposeTable.cellSet(); // 遍历输出 System.out.println("\n遍历输出开始----------------------------"); cells.forEach(cell -> System.out.println(cell.getRowKey() + ", " + cell.getColumnKey() + ", " + cell.getValue())); System.out.println("\n遍历输出结束----------------------------"); // 转换为嵌套的Map Map<String, Map<String, Integer>> rowMap = hashTable.rowMap(); System.out.println("\nTable RowMap: " + rowMap); Map<String, Map<String, Integer>> columnMap = hashTable.columnMap(); System.out.println("\nTable ColumnMap: " + columnMap);

  执行结果:

Copy
HashMap 获取值: 10 Hash Table: {xx公司={A部门=10, B部门=20, C部门=30}} Tree Table: {xx公司={A部门=10, B部门=20, C部门=30}} Immutable Table: {xx公司={C部门=30, B部门=20, A部门=10}} Table 获取值: 10 Table 删除值: 30 Table 列和值 映射: {A部门=10, B部门=20} Table 行和值 映射: {xx公司=10} Table Row key 集合: [xx公司] Table Column key 集合: [A部门, B部门] Table 值集合: [10, 20] Table 包含行: true Table 包含列: true Table 包含行和列: true Table 包含值: true 遍历输出开始---------------------------- A部门, xx公司, 10 B部门, xx公司, 20 遍历输出结束---------------------------- Table RowMap: {xx公司={A部门=10, B部门=20}} Table ColumnMap: {A部门={xx公司=10}, B部门={xx公司=20}}

4. BiMap - 双向映射Map#

  官方注释翻译:双映射(或“双向映射”)是一种保留其值及其键的唯一性的映射。此约束使双映射能够支持“反向视图”,即另一个双映射,其中包含与此双映射相同的条目,但具有相反的键和值。
  示例代码(需求: 数组和英文翻译):

Copy
// 创建BiMap, 底层为两个Hash表的Map BiMap<Integer, String> biMap = HashBiMap.create(); biMap.put(1, "one"); biMap.put(2, "two"); biMap.put(3, "three"); biMap.put(4, "four"); biMap.put(5, "five"); System.out.println("BiMap: " + biMap); // 创建不可变BiMap, 无法新增、更新或删除 BiMap<Object, Object> immutableBiMap = ImmutableBiMap.builder() .put(1, "one") .put(2, "two") .put(3, "three") .put(4, "four") .put(5, "five") .build(); System.out.println("\nImmutable BiMap: " + immutableBiMap); // 通过key获取value String value = biMap.get(1); System.out.println("\nBiMap 根据key获取value: " + value); Integer key = biMap.inverse().get("one"); System.out.println("\nBiMap 根据value获取key: " + key); // 翻转后修改 biMap.inverse().put("six", 6); // 返回双映射的逆视图, 并没有创建新对象, 还是之前的对象, 所以操作翻转后的BiMap会影响之前的BiMap System.out.println("\nBiMap 被影响: " + biMap); // 底层是HashMap, key不可重复 // value不可重复 try { biMap.put(11, "one"); } catch (Exception e) { System.err.println("BiMap 替换value异常: " + e.getMessage()); } // 翻转后key不能重复 try { biMap.inverse().put("first", 1); } catch (Exception e) { System.err.println("BiMap 替换key异常: " + e.getMessage()); } // key和value可为null biMap.put(null, null); System.out.println("\nBiMap 根据Null key获取Null value: " + biMap.get(null)); System.out.println("\nBiMap 根据Null value获取Null key: " + biMap.inverse().get(null)); // 强制替换key biMap.forcePut(11, "one"); System.out.println("\nBiMap 获取新key: " + biMap.inverse().get("one")); // values为Set集合 Set<String> values = biMap.values(); System.out.println("\nBiMap 不重复的value: " + values);

  执行结果:

Copy
BiMap: {1=one, 2=two, 3=three, 4=four, 5=five} Immutable BiMap: {1=one, 2=two, 3=three, 4=four, 5=five} BiMap 根据key获取value: one BiMap 根据value获取key: 1 BiMap 被影响: {1=one, 2=two, 3=three, 4=four, 5=five, 6=six} BiMap 替换value异常: value already present: one BiMap 替换key异常: key already present: 1 BiMap 根据Null key获取Null value: null BiMap 根据Null value获取Null key: null BiMap 获取新key: 11 BiMap 不重复的value: [two, three, four, five, six, null, one]

5. Multimap - 多重映射Map#

  官方注释翻译将键映射到值的集合,类似于 Map,但其中每个键可能与 多个 值相关联。
  示例代码(需求: 学生和各科选修课成绩):

Copy
// 创建Multimap, key为HashMap, value为ArrayList Multimap<String, Integer> arrayListMultimap = ArrayListMultimap.create(); arrayListMultimap.put("张三", 90); arrayListMultimap.put("张三", 80); arrayListMultimap.put("张三", 100); arrayListMultimap.put("李四", 88); System.out.println("Multimap key为HashMap, value为ArrayList: " + arrayListMultimap); // 创建Multimap, key为HashMap, value为HashSet Multimap<String, Integer> hashMultimap = HashMultimap.create(); hashMultimap.put("张三", 90); hashMultimap.put("张三", 80); hashMultimap.put("张三", 100); hashMultimap.put("李四", 88); System.out.println("\nMultimap key为HashMap, value为HashSet: " + hashMultimap); // 创建Multimap, key为LinkedHashMap, value为LinkedList Multimap<String, Integer> linkedListMultimap = LinkedListMultimap.create(); linkedListMultimap.put("张三", 90); linkedListMultimap.put("张三", 80); linkedListMultimap.put("张三", 100); linkedListMultimap.put("李四", 88); System.out.println("\nMultimap key为LinkedHashMap, value为LinkedList: " + linkedListMultimap); // 创建Multimap, key为LinkedHashMap, value为LinkedHashMap Multimap<String, Integer> linkedHashMultimap = LinkedHashMultimap.create(); linkedHashMultimap.put("张三", 90); linkedHashMultimap.put("张三", 80); linkedHashMultimap.put("张三", 100); linkedHashMultimap.put("李四", 88); System.out.println("\nMultimap key为LinkedHashMap, value为LinkedHashMap: " + linkedHashMultimap); // 创建Multimap, key为TreeMap, value为TreeSet Multimap<String, Integer> treeMultimap = TreeMultimap.create(); treeMultimap.put("张三", 90); treeMultimap.put("张三", 80); treeMultimap.put("张三", 100); treeMultimap.put("李四", 88); System.out.println("\nMultimap key为TreeMap, value为TreeSet: " + treeMultimap); // 创建不可变Multimap, 无法新增、更新或删除, key为ImmutableMap, value为ImmutableList Multimap<String, Integer> immutableListMultimap = ImmutableListMultimap.<String, Integer>builder() .put("张三", 90) .put("张三", 80) .put("张三", 100) .put("李四", 88) .build(); System.out.println("\nMultimap key为ImmutableMap, value为ImmutableList: " + immutableListMultimap); // 创建不可变Multimap, 无法新增、更新或删除, key为ImmutableMap, value为ImmutableSet Multimap<String, Integer> immutableSetMultimap = ImmutableSetMultimap.<String, Integer>builder() .put("张三", 90) .put("张三", 80) .put("张三", 100) .put("李四", 88) .build(); System.out.println("\nMultimap key为ImmutableMap, value为ImmutableSet: " + immutableSetMultimap); // 获取值 Collection<Integer> values = arrayListMultimap.get("张三"); System.out.println("\nMultimap 获取值集合: " + values); // 获取不存在key的值, 返回的是空集合, 而不是null Collection<Integer> valuesByNotExistsKey = arrayListMultimap.get("王五"); System.out.println("\nMultimap 获取不存在的Key值集合: " + valuesByNotExistsKey); // 获取值集合添加值 // 返回的是多重映射中关联的值的视图集合, 并没有创建新对象, 还是之前的对象, 所以操作值集合会影响之前的Multimap values.add(60); System.out.println("\nMultimap 被影响: " + arrayListMultimap); // 获取大小 System.out.println("\nMultimap 大小:" + arrayListMultimap.size()); // 判断是否为空 System.out.println("\nMultimap 是否为空: " + arrayListMultimap.isEmpty()); // 包含key System.out.println("\nMultimap 包含key: " + arrayListMultimap.containsKey("张三")); // 包含value System.out.println("\nMultimap 包含value: " + arrayListMultimap.containsValue(60)); // 包含key-value键值对 System.out.println("\nMultimap 包含key-value对: " + arrayListMultimap.containsEntry("张三", 60)); // 替换value arrayListMultimap.replaceValues("张三", Arrays.asList(10, 20, 30)); System.out.println("\nMultimap 替换value: " + arrayListMultimap); // 根据key-value删除 arrayListMultimap.remove("张三", 10); System.out.println("\nMultimap 根据key-value删除: " + arrayListMultimap); // 根据key删除 Collection<Integer> removeAll = arrayListMultimap.removeAll("张三"); System.out.println("\nMultimap 根据key删除: " + removeAll); // 获取key集合 Set<String> keySet = arrayListMultimap.keySet(); System.out.println("\nMultimap 获取key集合(HashSet): " + keySet); Multiset<String> keys = arrayListMultimap.keys(); System.out.println("\nMultimap 获取key集合(MultiSet): " + keys); // 获取所有的key-value Collection<Map.Entry<String, Integer>> entries = arrayListMultimap.entries(); System.out.println("\n遍历key-value开始--------------------------"); entries.forEach(entry -> System.out.println(entry.getKey() + " : " + entry.getValue())); System.out.println("\n遍历key-value结束--------------------------"); // 转换为Map<K, Collection<V>> Map<String, Collection<Integer>> collectionMap = arrayListMultimap.asMap(); System.out.println("\nMultimap 转换为Map<K, Collection<V>>: " + collectionMap);

  执行结果:

Copy
Multimap key为HashMap, value为ArrayList: {李四=[88], 张三=[90, 80, 100]} Multimap key为HashMap, value为HashSet: {李四=[88], 张三=[80, 100, 90]} Multimap key为LinkedHashMap, value为LinkedList: {张三=[90, 80, 100], 李四=[88]} Multimap key为LinkedHashMap, value为LinkedHashMap: {张三=[90, 80, 100], 李四=[88]} Multimap key为TreeMap, value为TreeSet: {张三=[80, 90, 100], 李四=[88]} Multimap key为ImmutableMap, value为ImmutableList: {张三=[90, 80, 100], 李四=[88]} Multimap key为ImmutableMap, value为ImmutableSet: {张三=[90, 80, 100], 李四=[88]} Multimap 获取值集合: [90, 80, 100] Multimap 获取不存在的Key值集合: [] Multimap 被影响: {李四=[88], 张三=[90, 80, 100, 60]} Multimap 大小:5 Multimap 是否为空: false Multimap 包含key: true Multimap 包含value: true Multimap 包含key-value对: true Multimap 替换value: {李四=[88], 张三=[10, 20, 30]} Multimap 根据key-value删除: {李四=[88], 张三=[20, 30]} Multimap 根据key删除: [20, 30] Multimap 获取key集合(HashSet): [李四] Multimap 获取key集合(MultiSet): [李四] 遍历key-value开始-------------------------- 李四 : 88 遍历key-value结束-------------------------- Multimap 转换为Map<K, Collection<V>>: {李四=[88]}

6. RangeMap - 范围映射Map#

  官方注释翻译:从不相交的非空范围到非 null 值的映射。查询查找与包含指定键的范围(如果有)关联的值。
  示例代码(需求:考试成绩分类):

Copy
// if-else int score = 88; String rank; if (0 <= score && score < 60) { rank = "不及格"; } else if (60 <= score && score <= 84) { rank = "及格"; } else if (84 < score && score <= 100) { rank = "优秀"; } else { rank = "无效"; } System.out.println("if-else 获取值: " + rank); // 创建RangeMap, 基于TreeMap(红黑树)实现 RangeMap<Integer, String> treeRangeMap = TreeRangeMap.create(); treeRangeMap.put(Range.closedOpen(0, 60), "不及格"); treeRangeMap.put(Range.closed(60, 84), "及格"); treeRangeMap.put(Range.openClosed(84, 100), "优秀"); treeRangeMap.put(Range.lessThan(0), "无效"); treeRangeMap.put(Range.greaterThan(100), "无效"); rank = treeRangeMap.get(score); System.out.println("\nRangeMap 获取值: " + rank); // 创建不可变RangeMap, 无法新增、更新或删除 ImmutableRangeMap<Integer, String> immutableRangeMap = ImmutableRangeMap.<Integer, String>builder() .put(Range.closedOpen(0, 60), "不及格") .put(Range.closed(60, 84), "及格") .put(Range.openClosed(84, 100), "优秀") .put(Range.lessThan(0), "无效") .put(Range.greaterThan(100), "无效") .build(); rank = immutableRangeMap.get(score); System.out.println("\nImmutableRangeMap 获取值: " + rank); // 获取key-value对 Map.Entry<Range<Integer>, String> entry = treeRangeMap.getEntry(88); System.out.println("\nRangeMap 获取key-value对: " + entry.getKey() + " : " + entry.getValue()); // 返回不可变的升序的Map Map<Range<Integer>, String> asMapOfRanges = treeRangeMap.asMapOfRanges(); System.out.println("\nRangeMap 不可变的升序的Map: " + asMapOfRanges); // 返回不可变的降序的Map Map<Range<Integer>, String> asDescendingMapOfRanges = treeRangeMap.asDescendingMapOfRanges(); System.out.println("\nRangeMap 不可变的降序的Map: " + asDescendingMapOfRanges); // 相连范围合并 RangeMap<Integer, String> treeRangeMap2 = TreeRangeMap.create(); treeRangeMap2.putCoalescing(Range.closedOpen(0, 60), "不及格"); treeRangeMap2.putCoalescing(Range.closed(60, 84), "及格"); treeRangeMap2.putCoalescing(Range.openClosed(84, 100), "及格"); // 或者 [60..84]范围合并 treeRangeMap2.putCoalescing(Range.lessThan(0), "无效"); treeRangeMap2.putCoalescing(Range.greaterThan(100), "无效"); System.out.println("\nRangeMap 不合并相连范围: " + treeRangeMap.asMapOfRanges()); System.out.println("RangeMap 合并相连范围: " + treeRangeMap2.asMapOfRanges()); // 最小范围 Range<Integer> span = treeRangeMap.span(); System.out.println("\nRangeMap 最小范围: " + span); // 子范围Map RangeMap<Integer, String> subRangeMap = treeRangeMap.subRangeMap(Range.closed(70, 90)); System.out.println("\nRangeMap 子范围Map: " + subRangeMap); // 合并范围 treeRangeMap.merge(Range.closed(60, 100), "及格", (s, s2) -> s2); System.out.println("\nRangeMap 合并Map: " + treeRangeMap); // 移除范围 treeRangeMap.remove(Range.open(90, 95)); System.out.println("\nRangeMap 移除范围: " + treeRangeMap); // 清除所有范围 treeRangeMap.clear(); System.out.println("\nRangeMap 清除所有范围: " + treeRangeMap);

  执行结果:

Copy
if-else 获取值: 优秀 RangeMap 获取值: 优秀 ImmutableRangeMap 获取值: 优秀 RangeMap 获取key-value对: (84..100] : 优秀 RangeMap 不可变的升序的Map: {(-∞..0)=无效, [0..60)=不及格, [60..84]=及格, (84..100]=优秀, (100..+∞)=无效} RangeMap 不可变的降序的Map: {(100..+∞)=无效, (84..100]=优秀, [60..84]=及格, [0..60)=不及格, (-∞..0)=无效} RangeMap 不合并相连范围: {(-∞..0)=无效, [0..60)=不及格, [60..84]=及格, (84..100]=优秀, (100..+∞)=无效} RangeMap 合并相连范围: {(-∞..0)=无效, [0..60)=不及格, [60..100]=及格, (100..+∞)=无效} RangeMap 最小范围: (-∞..+∞) RangeMap 子范围Map: {[70..84]=及格, (84..90]=优秀} RangeMap 合并Map: [(-∞..0)=无效, [0..60)=不及格, [60..84]=及格, (84..100]=及格, (100..+∞)=无效] RangeMap 移除范围: [(-∞..0)=无效, [0..60)=不及格, [60..84]=及格, (84..90]=及格, [95..100]=及格, (100..+∞)=无效] RangeMap 清除所有范围: []

7. ClassToInstanceMap - 类型映射到实例Map#

  官方注释翻译:映射,其每个条目将一个 Java 原始类型 映射到该类型的实例。除了实现 Map之外,还提供额外的类型安全操作 putInstance 和 getInstance 。与任何其他 Map<Class, Object>映射一样,此映射可能包含基元类型的条目,并且基元类型及其相应的包装器类型可以映射到不同的值。
  示例代码(需求:缓存Bean(不交给Spring管理,自己管理Bean)):

Copy
class UserBean { private final Integer id; private final String username; public UserBean(Integer id, String username) { this.id = id; this.username = username; } @Override public String toString() { return "UserBean{" + "id=" + id + ", username='" + username + '\'' + '}'; } } // 创建Bean UserBean userBean = new UserBean(1, "张三"); // HashMap HashMap<Class, Object> hashMap = new HashMap<>(); hashMap.put(UserBean.class, userBean); // 获取值,需要强转 UserBean value = (UserBean) hashMap.get(UserBean.class); System.out.println("HashMap 获取对象实例: " + value); System.out.println("HashMap 获取对象实例等于创建的Bean: " + (value == userBean)); // 创建ClassToInstanceMap ClassToInstanceMap<Object> classToInstanceMap = MutableClassToInstanceMap.create(); classToInstanceMap.putInstance(UserBean.class, userBean); // 获取值,无需强转 UserBean value2 = classToInstanceMap.getInstance(UserBean.class); System.out.println("\nClassToInstanceMap 获取对象实例: " + value2); System.out.println("ClassToInstanceMap 获取对象实例等于创建的Bean: " + (value2 == userBean)); // 创建不可变ClassToInstanceMap, 无法新增、更新或删除 ClassToInstanceMap<UserBean> immutableClassToInstanceMap = ImmutableClassToInstanceMap.<UserBean>builder() .put(UserBean.class, userBean) .build(); // 获取值,无需强转 UserBean value3 = immutableClassToInstanceMap.getInstance(UserBean.class); System.out.println("\nImmutableClassToInstanceMap 获取对象实例: " + value3); System.out.println("ImmutableClassToInstanceMap 获取对象实例等于创建的Bean: " + (value3 == userBean)); // 限制类型,避免使用HashMap存储对象时,因为使用Object值类型而在添加缓存时需要今天类型校验 ClassToInstanceMap<Collection> classToInstanceMap1 = MutableClassToInstanceMap.create(); classToInstanceMap1.put(ArrayList.class, new ArrayList()); classToInstanceMap1.put(HashSet.class, new HashSet()); // 编译保存: 'put(java.lang.Class<? extends java.util.@org.checkerframework.checker.nullness.qual.NonNull Collection>, java.util.Collection)' in 'com.google.common.collect.MutableClassToInstanceMap' cannot be applied to '(java.lang.Class<java.util.HashMap>, java.util.HashMap)' // classToInstanceMap1.put(HashMap.class, new HashMap());

  执行结果:

Copy
HashMap 获取对象实例: UserBean{id=1, username='张三'} HashMap 获取对象实例等于创建的Bean: true ClassToInstanceMap 获取对象实例: UserBean{id=1, username='张三'} ClassToInstanceMap 获取对象实例等于创建的Bean: true ImmutableClassToInstanceMap 获取对象实例: UserBean{id=1, username='张三'} ImmutableClassToInstanceMap 获取对象实例等于创建的Bean: true
posted @   C3Stones  阅读(2699)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示
CONTENTS