集合
1、Multimap(一键多值映射)
com.google.common.collect.Multimap 是 Guava 提供的一对多映射集合。它解决了原生 Java 中 Map<K, List> 或 Map<K, Set> 维护繁琐的问题(如手动初始化集合、判空、移除元素等),将一个键对应多个值的逻辑封装为统一且简洁的接口。
核心实现类分类
Multimap 是一个接口,针对值的底层实现方式不同,Guava 提供了以下主要实现:
| 实现类 | 值的存储类型 (get(key) 的返回值) | 允许重复值 | 关联的原生结构 |
|---|---|---|---|
ArrayListMultimap | List<V> | 是 | Map<K, List<V>> |
HashMultimap | Set<V> | 否 | Map<K, Set<V>> |
LinkedListMultimap | List<V>(双向链表,保持插入顺序) | 是 | Map<K, LinkedList<V>> |
TreeMultimap | NavigableSet<V>(有序) | 否 | TreeMap<K, TreeSet<V>> |
LinkedHashMultimap | Set<V>(保持键和值的插入顺序) | 否 | LinkedHashMap<K, LinkedHashSet<V>> |
1.1、核心API
1. 元素添加与修改
| API 方法 | 说明 |
|---|---|
put(K, V) | 为指定 key 添加一个 value。若 key 不存在则自动创建内部集合 |
putAll(K, Iterable<V>) | 为指定 key 批量添加多个 value |
putAll(Multimap<K, V>) | 将另一个 Multimap 的内容完全合并进来 |
replaceValues(K, Iterable<V>) | 替换指定 key 的所有旧值,返回旧值的集合 |
2. 元素获取与查询
| API 方法 | 说明 |
|---|---|
get(K) | 获取指定 key 对应的集合。即使 key 不存在,也会返回一个非 null 的空集合 |
containsKey(Object) | 检查是否存在指定的 key |
containsValue(Object) | 检查是否存在指定的 value |
containsEntry(Object, Object) | 检查是否存在指定的键值对 (K, V) |
size() | 返回所有键值对的总数量(不是 key 的数量) |
isEmpty() | 是否完全没有键值对 |
3. 元素移除
| API 方法 | 说明 |
|---|---|
remove(Object key, Object value) | 仅移除指定的某一个 (key, value) 键值对 |
removeAll(Object key) | 移除指定 key 关联的所有 value,并返回被移除的集合 |
clear() | 清空整个 Multimap |
4. 视图导出(Views)
| API 方法 | 说明 |
|---|---|
keySet() | 返回所有 key 的 Set 集合 |
keys() | 返回包含重复 key 的 Multiset(可统计每个 key 对应的 value 数量) |
values() | 返回所有 value 扁平化后的集合平铺视图 |
entries() | 返回所有 Map.Entry<K, V> 键值对 |
asMap() | 将 Multimap<K, V> 转换为原生 Map<K, Collection<V>> 的视图 |
1.2、使用示例
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multiset;
import java.util.Collection;
import java.util.Map;
public class MultimapExample {
public static void main(String[] args) {
// 1. 创建 ArrayListMultimap (允许重复的 value)
Multimap<String, String> fruitColorMap = ArrayListMultimap.create();
// 添加键值对 (无需手动创建 List)
fruitColorMap.put("Red", "Apple");
fruitColorMap.put("Red", "Strawberry");
fruitColorMap.put("Red", "Apple"); // ArrayListMultimap 允许重复元素
fruitColorMap.put("Yellow", "Banana");
fruitColorMap.put("Yellow", "Lemon");
System.out.println("--- 1. 基础添加与获取 ---");
// get(key) 总是返回非 null 的集合
Collection<String> redFruits = fruitColorMap.get("Red");
System.out.println("Red 颜色的水果: " + redFruits);
// 输出: [Apple, Strawberry, Apple]
// 不存在的 key 返回空集合,不会抛出 NPE
System.out.println("Green 颜色的水果: " + fruitColorMap.get("Green"));
// 输出: []
System.out.println("总键值对数量 (size): " + fruitColorMap.size());
// 输出: 5
// 2. 元素移除
System.out.println("\n--- 2. 移除操作 ---");
// 单个移除 (仅移除一个匹配的 "Apple")
fruitColorMap.remove("Red", "Apple");
System.out.println("移除一个 Red-Apple 后的 Red 集合: " + fruitColorMap.get("Red"));
// 批量移除整个 key 关联的所有元素
Collection<String> removedYellow = fruitColorMap.removeAll("Yellow");
System.out.println("被移除的 Yellow 集合: " + removedYellow);
System.out.println("移除后的 Multimap: " + fruitColorMap);
// 3. 多种视图展示 (keys, keySet, asMap)
System.out.println("\n--- 3. 视图展示 ---");
Multimap<String, String> studentCourses = HashMultimap.create();
studentCourses.put("Alice", "Math");
studentCourses.put("Alice", "Physics");
studentCourses.put("Bob", "Math");
// keys() 返回 Multiset,可直接查看每个 key 对应的 value 计数
Multiset<String> keysMultiset = studentCourses.keys();
System.out.println("Alice 选课数量: " + keysMultiset.count("Alice")); // 输出: 2
// asMap() 转换为标准的 Java Map 视图
Map<String, Collection<String>> mapAdapter = studentCourses.asMap();
System.out.println("Map 视图: " + mapAdapter);
// 对 asMap() 返回的 map 进行 remove 操作,会同步改变原 Multimap
mapAdapter.remove("Bob");
System.out.println("通过 asMap 移除 Bob 后原 Multimap: " + studentCourses);
// 4. 视图修改同步 (Live View 特性)
System.out.println("\n--- 4. 动态视图修改 ---");
Collection<String> aliceCourses = studentCourses.get("Alice");
aliceCourses.add("Chemistry"); // 直接对 get() 返回的集合进行修改
System.out.println("修改 get 返回的集合后,原 Multimap 的 Alice 课程: " + studentCourses.get("Alice"));
// 输出: [Physics, Math, Chemistry] (取决于 HashMultimap 内部无序存储)
}
}
--- 1. 基础添加与获取 ---
Red 颜色的水果: [Apple, Strawberry, Apple]
Green 颜色的水果: []
总键值对数量 (size): 5
--- 2. 移除操作 ---
移除一个 Red-Apple 后的 Red 集合: [Strawberry, Apple]
被移除的 Yellow 集合: [Banana, Lemon]
移除后的 Multimap: {Red=[Strawberry, Apple]}
--- 3. 视图展示 ---
Alice 选课数量: 2
Map 视图: {Bob=[Math], Alice=[Math, Physics]}
通过 asMap 移除 Bob 后原 Multimap: {Alice=[Math, Physics]}
--- 4. 动态视图修改 ---
修改 get 返回的集合后,原 Multimap 的 Alice 课程: [Math, Physics, Chemistry]
1.3、注意事项
- get(key) 的“动态视图(Live View)”特性:
multimap.get(key) 返回的集合是原 Multimap 的实时视图。对该返回集合直接执行 .add() 或 .remove(),会直接修改原 Multimap 的数据。 - size() 与 keySet().size() 的区别:
- size() 返回的是所有 value 的总数量。
- keySet().size() 返回的是不重复 key 的数量。
- 避免混淆 asMap():
- asMap() 提供了 Map<K, Collection> 接口,但调用 asMap().get(badKey) 对不存在的 key 会返回 null(符合 Java 标准 Map 语义),而 multimap.get(badKey) 则返回空集合。
2、Multiset(可重复无序集合 / 计数器)
com.google.common.base.Multiset 是 Guava 提供的可重复元素集合(也称 Bag)。它继承自 Java 的 java.util.Collection 接口,结合了 List 和 Set 的特性:
- 像 List 一样:允许存储重复的元素。
- 像 Set 一样:不保证元素的顺序(除非使用有序实现类),且提供了方便的计数和去重视图。
在需要统计元素出现次数(如词频统计、库存计数)的场景下,使用 Multiset 可以彻底取代手写 Map<E, Integer>。
核心实现类分类
| 实现类 | 关联的原生结构 | 说明 |
|---|---|---|
HashMultiset | HashMap | 最常用,元素无序,基于哈希表实现 |
TreeMultiset | TreeMap | 元素按自然顺序或指定的 Comparator 排序 |
LinkedHashMultiset | LinkedHashMap | 保持元素的首次插入顺序 |
ConcurrentHashMultiset | ConcurrentHashMap | 线程安全的实现,适用于并发计数 |
2.1、核心API
1. 计数与获取
| API 方法 | 返回类型 | 说明 |
|---|---|---|
count(Object element) | int | 返回指定元素在集合中出现的总次数 |
size() | int | 返回集合中所有元素的总数量(包含重复项) |
elementSet() | Set<E> | 返回去重后的元素集合 Set 视图 |
entrySet() | Set<Entry<E>> | 返回每个元素及其对应计数的键值对集合 |
2. 添加与设置
| API 方法 | 返回类型 | 说明 |
|---|---|---|
add(E element) | boolean | 增加一个元素(数量 +1) |
add(E element, int occurrences) | int | 为指定元素增加指定的出现次数,返回增加前的旧数量 |
setCount(E element, int count) | int | 直接设置指定元素的数量为 count,返回修改前的旧数量 |
setCount(E element, int oldCount, int newCount) | boolean | CAS 替换:仅当旧数量等于 oldCount 时才更新为 newCount |
3. 移除
| API 方法 | 返回类型 | 说明 |
|---|---|---|
remove(Object element) | boolean | 减少一个指定元素(数量 -1) |
remove(Object element, int occurrences) | int | 减少指定元素的出现次数,返回减少前的旧数量 |
removeAll(Collection<?>) | boolean | 彻底清空集合中包含在指定集合里的元素(数量置 0) |
2.2、使用示例
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import java.util.Arrays;
import java.util.List;
public class MultisetDetailExample {
public static void main(String[] args) {
// 1. 创建 HashMultiset
Multiset<String> wordMultiset = HashMultiset.create();
// 2. 模拟词频统计:基础添加 add(E) 与批量添加 add(E, occurrences)
List<String> words = Arrays.asList("apple", "banana", "apple", "orange");
wordMultiset.addAll(words); // 批量添加 List 中的所有元素
// 直接为某个元素设置额外的出现次数
wordMultiset.add("banana", 3); // 为 "banana" 再增加 3 次
wordMultiset.add("watermelon", 1);
System.out.println("--- 1. 元素计数与总数 ---");
System.out.println("apple 出现次数: " + wordMultiset.count("apple")); // 输出: 2
System.out.println("banana 出现次数: " + wordMultiset.count("banana")); // 输出: 4
System.out.println("grapes 出现次数: " + wordMultiset.count("grapes")); // 未添加的元素返回 0,不报 NPE
// size() 返回所有元素计数的总和 (2 + 4 + 1 + 1 = 8)
System.out.println("所有元素总数 (size): " + wordMultiset.size()); // 输出: 8
// 3. 元素设置与修改 (setCount)
System.out.println("\n--- 2. 修改与覆盖计数 ---");
// 直接修改 "apple" 的数量为 10,返回修改前的旧数量 (2)
int oldCount = wordMultiset.setCount("apple", 10);
System.out.println("修改前 apple 数量: " + oldCount + ",修改后数量: " + wordMultiset.count("apple"));
// 条件替换:只有当前 "watermelon" 数量为 1 时,才更新为 5
boolean isReplaced = wordMultiset.setCount("watermelon", 1, 5);
System.out.println("watermelon 替换是否成功: " + isReplaced + ",最新数量: " + wordMultiset.count("watermelon"));
// 4. 元素移除 (remove)
System.out.println("\n--- 3. 元素移除 ---");
// 减少 2 个 apple
wordMultiset.remove("apple", 2);
System.out.println("移除 2 个 apple 后剩余数量: " + wordMultiset.count("apple")); // 输出: 8
// 单个移除 remove(E) 只把数量 -1
wordMultiset.remove("banana");
System.out.println("移除 1 个 banana 后剩余数量: " + wordMultiset.count("banana")); // 输出: 3
// 5. 视图与遍历 (elementSet & entrySet)
System.out.println("\n--- 4. 视图与遍历 ---");
// elementSet():获取不重复的元素集合 (Set)
System.out.print("去重后的元素列表: ");
for (String element : wordMultiset.elementSet()) {
System.out.print(element + " ");
}
System.out.println();
// entrySet():获取每个元素与其出现的次数键值对
System.out.println("详细词频明细:");
for (Multiset.Entry<String> entry : wordMultiset.entrySet()) {
System.out.println(" 词汇: " + entry.getElement() + " -> 次数: " + entry.getCount());
}
}
}
--- 1. 元素计数与总数 ---
apple 出现次数: 2
banana 出现次数: 4
grapes 出现次数: 0
所有元素总数 (size): 8
--- 2. 修改与覆盖计数 ---
修改前 apple 数量: 2,修改后数量: 10
watermelon 替换是否成功: true,最新数量: 5
--- 3. 元素移除 ---
移除 2 个 apple 后剩余数量: 8
移除 1 个 banana 后剩余数量: 3
--- 4. 视图与遍历 ---
去重后的元素列表: banana orange apple watermelon
详细词频明细:
词汇: banana -> 次数: 3
词汇: orange -> 次数: 1
词汇: apple -> 次数: 8
词汇: watermelon -> 次数: 5
2.3、使用注意事项
- size() 与 elementSet().size() 的区别:
- multiset.size():返回所有元素计数的总和(例如包含 10 个 “apple” 和 3 个 “banana”,size() 为 13)。
- multiset.elementSet().size():返回去重后不同元素的种类数量(即 2)。
- 非严格的 Map 替代:
- 虽然 Multiset 看起来像 Map<E, Integer>,但它实现了 Collection 接口。当你对 Multiset 执行 iterator() 遍历时,迭代器会逐个输出每一个重复的元素(即 10 个 “apple” 会被循环打印 10 次),而不是只循环打印 1 次。
- 不能添加 count < 0 的元素:
- 元素的计数永远大于等于 0。如果调用 remove(element, occurrences) 导致数量小于等于 0,该元素会自动从 elementSet() 视图中被清除。
3、BiMap(双向映射 Map)
com.google.common.collect.BiMap 是 Guava 提供的双向映射(Bi-directional Map)接口。它继承自 Java 标准的 java.util.Map,核心特点是保证 键(Key)和值(Value)的双向唯一性。
通过 BiMap,你可以轻松地实现 Key 到 Value 以及 Value 到 Key 的双向正反向查找,而无需维护两套独立的 Map。
核心实现类分类
根据底层存储机制的不同,Guava 提供了以下主要的 BiMap 实现:
| 实现类 | 底层结构 | 是否有序 | Key/Value 是否允许 null |
|---|---|---|---|
HashBiMap | 哈希表(两个 HashMap 结合) | 无序 | 允许 null |
ImmutableBiMap | 不可变双向映射 | 保持插入顺序 | 不允许 null |
EnumBiMap | 两个 EnumMap 结合 | 按枚举声明顺序 | 不允许 null (Key/Value 必须为枚举) |
EnumHashBiMap | Key 为枚举,Value 为哈希表 | 按 Key 枚举声明顺序 | Key 必须为枚举 |
3.1、核心API
1. 双向特有 API
| API 方法 | 返回类型 | 说明 |
|---|---|---|
inverse() | BiMap<V, K> | 反转视图:返回一个 Key 和 Value 颠倒的新 BiMap |
forcePut(K, V) | V | 强制添加:若传入的 Value 已存在,会直接覆盖原有的 Key-Value 映射 |
values() | Set<V> | 重写了标准 Map 的方法:因为 Value 唯一,所以返回值类型为 Set 而非 Collection |
2. 标准 Map 继承 API
BiMap 继承了 Map 接口,因此支持所有常见的 Map 操作:
| API 方法 | 说明 |
|---|---|
put(K, V) | 普通添加:若指定的 Value 已存在,会直接抛出 IllegalArgumentException |
putAll(Map) | 批量添加:若存在重复的 Value,同样会抛出异常 |
get(Object key) | 根据 Key 获取 Value |
remove(Object key) | 根据 Key 移除映射 |
containsKey(Object) / containsValue(Object) | 查询 Key 或 Value 是否存在 |
3.2、使用示例
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
public class BiMapDetailExample {
public static void main(String[] args) {
// 1. 创建 HashBiMap (用于存储 用户ID <-> 用户名)
BiMap<Integer, String> userIdToName = HashBiMap.create();
// 基础元素添加
userIdToName.put(101, "Alice");
userIdToName.put(102, "Bob");
userIdToName.put(103, "Charlie");
System.out.println("--- 1. 正向获取与基础操作 ---");
System.out.println("ID 101 的用户名: " + userIdToName.get(101)); // 输出: Alice
// 2. 验证 Value 唯一性机制 (put vs forcePut)
System.out.println("\n--- 2. Value 唯一性校验与强制覆盖 ---");
try {
// 尝试把 ID 104 也绑定到已有名称 "Alice" 上
userIdToName.put(104, "Alice");
} catch (IllegalArgumentException e) {
System.out.println("捕获期望异常: Value 'Alice' 已被其他 Key 占用!");
}
// 使用 forcePut:强制将 "Alice" 绑定到 104 上,原有的 101 -> "Alice" 会被直接清除!
userIdToName.forcePut(104, "Alice");
System.out.println("使用 forcePut 覆盖后 ID 101 对应的 Value: " + userIdToName.get(101)); // 输出: null
System.out.println("最新的 BiMap 映射关系: " + userIdToName);
// 3. inverse() 反转视图与双向查找
System.out.println("\n--- 3. inverse() 反转视图使用 ---");
// 获取反转映射:Name -> ID
BiMap<String, Integer> nameToUserId = userIdToName.inverse();
System.out.println("根据用户名 'Bob' 查找 ID: " + nameToUserId.get("Bob")); // 输出: 102
System.out.println("根据用户名 'Alice' 查找 ID: " + nameToUserId.get("Alice")); // 输出: 104
// 4. 动态视图同步特性 (Live View)
System.out.println("\n--- 4. 正反视图修改同步 ---");
// 修改反转视图 nameToUserId,原 userIdToName 会同步修改
nameToUserId.remove("Bob"); // 在反转视图中移除 "Bob"
System.out.println("通过反向视图移除 'Bob' 后,原 BiMap 依然包含 102 吗? "
+ userIdToName.containsKey(102)); // 输出: false
// 5. values() 返回类型为 Set
System.out.println("\n--- 5. values() 方法演练 ---");
// 由于 Value 绝对不重复,BiMap 的 values() 直接返回 Set<V>
System.out.println("所有用户名的 Set 集合: " + userIdToName.values());
}
}
--- 1. 正向获取与基础操作 ---
ID 101 的用户名: Alice
--- 2. Value 唯一性校验与强制覆盖 ---
捕获期望异常: Value 'Alice' 已被其他 Key 占用!
使用 forcePut 覆盖后 ID 101 对应的 Value: null
最新的 BiMap 映射关系: {102=Bob, 103=Charlie, 104=Alice}
--- 3. inverse() 反转视图使用 ---
根据用户名 'Bob' 查找 ID: 102
根据用户名 'Alice' 查找 ID: 104
--- 4. 正反视图修改同步 ---
通过反向视图移除 'Bob' 后,原 BiMap 依然包含 102 吗? false
--- 5. values() 方法演练 ---
所有用户名的 Set 集合: [Charlie, Alice]
3.3、注意事项
- put 与 forcePut 的选型:
- 使用 put(K, V) 时,Guava 会严格保护数据的双向唯一性,一旦 Value 冲突立刻报错。
- 使用 forcePut(K, V) 时,旧的关联会被悄悄删除(如示例中 101 -> Alice 被直接移除),使用时需谨慎评估是否会丢数据。
- inverse() 的“动态视图(Live View)”特性:
- biMap.inverse() 并没有重新创建并复制一个新的 BiMap,它只是对原 BiMap 包装了一层反向操作。对反转视图的任何增删改,都会实时作用于原 BiMap,反之亦然。
- 性能优势:
- 相比手写两个 Map<K, V> 和 Map<V, K> 维护,BiMap 内部维护了指针关联,不仅节约了空间,且在删除和更新时能确保两端的绝对一致性。
4、Table(双键二维表)
com.google.common.collect.Table 是 Guava 提供的双键二维表结构(也称二维 Map)。它相当于 Java 标准库中的 Map<R, Map<C, V>>,通过 行键(RowKey) 和 列键(ColumnKey) 共同唯一定位一个 值(Value)。
Table 能够帮助你摆脱繁琐的多层嵌套 Map(如判空、逐层初始化等),在处理矩阵数据、Excel 单元格映射、权限矩阵或多维度交叉索引时非常高效。
核心实现类分类
| 实现类 | 关联的原生结构 | 说明 |
|---|---|---|
HashBasedTable | HashMap<R, HashMap<C, V>> | 最常用,行和列均基于哈希表实现,无序 |
TreeBasedTable | TreeMap<R, TreeMap<C, V>> | 行和列均保持自然排序或指定 Comparator |
ArrayTable | 二维数组 | 要求在创建时指定固定的行和列集合,适合密集矩阵,性能极高 |
ImmutableTable | 不可变二维表 | 构建后不可修改,内存占用小且线程安全 |
4.1、核心API
1. 元素添加与移除
| API 方法 | 说明 |
|---|---|
put(R rowKey, C columnKey, V value) | 插入单元格数据。若位置已存在值则替换并返回旧值 |
putAll(Table<R, C, V>) | 批量导入另一个 Table 的所有单元格 |
remove(R rowKey, C columnKey) | 移除指定坐标处的单元格数据,并返回被移除的值 |
clear() | 清空表中的所有单元格 |
2. 元素查询与校验
| API 方法 | 返回类型 | 说明 |
|---|---|---|
get(R rowKey, C columnKey)" | V | 获取指定行列的值,若不存在则返回 null |
contains(R rowKey, C columnKey) | boolean | 判断指定坐标处是否存在单元格 |
containsRow(R rowKey) | boolean | 判断是否存在指定的行 |
containsColumn(C columnKey) | boolean | 判断是否存在指定的列 |
containsValue(V value) | boolean | 判断表中是否包含指定的值 |
isEmpty() / size() | boolean / int | 判断表是否为空 / 返回单元格(Cell)的总数量 |
3. 视图导出(Views)
| API 方法 | 返回类型 | 说明 |
|---|---|---|
row(R rowKey) | Map<C, V> | 获取指定行的所有列与值的 Map 视图 |
column(C columnKey) | Map<R, V> | 获取指定列的所有行与值的 Map 视图(转置提取) |
rowMap() | Map<R, Map<C, V>> | 导出为按行分组的嵌套 Map 视图 |
columnMap() | Map<C, Map<R, V>> | 导出为按列分组的嵌套 Map 视图 |
rowKeySet() | Set<R> | 获取所有行键的集合 |
columnKeySet() | Set<C> | 获取所有列键的集合 |
cellSet() | Set<Table.Cell<R,C,V>> | 获取所有单元格的三元组集合 (getRowKey(), getColumnKey(), getValue())" |
4.2、使用示例
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import java.util.Map;
import java.util.Set;
public class TableDetailExample {
public static void main(String[] args) {
// 1. 创建 HashBasedTable (模拟学生成绩表:行=学生姓名, 列=学科, 值=分数)
Table<String, String, Integer> scoreTable = HashBasedTable.create();
// 基础数据插入
scoreTable.put("Alice", "Math", 95);
scoreTable.put("Alice", "English", 88);
scoreTable.put("Bob", "Math", 80);
scoreTable.put("Bob", "English", 92);
scoreTable.put("Charlie", "Math", 100);
System.out.println("--- 1. 单点查找与基础信息 ---");
// 获取指定单元格数据
System.out.println("Alice 的 Math 成绩: " + scoreTable.get("Alice", "Math")); // 输出: 95
System.out.println("Charlie 的 English 成绩: " + scoreTable.get("Charlie", "English")); // 不存在返回 null
System.out.println("单元格总数 (size): " + scoreTable.size()); // 输出: 5
System.out.println("所有学生 (rowKeySet): " + scoreTable.rowKeySet()); // 输出: [Alice, Bob, Charlie]
System.out.println("所有学科 (columnKeySet): " + scoreTable.columnKeySet()); // 输出: [Math, English]
// 2. 按行切片获取 (row)
System.out.println("\n--- 2. 按行提取数据 (row) ---");
Map<String, Integer> aliceScores = scoreTable.row("Alice");
System.out.println("Alice 的所有学科成绩 Map: " + aliceScores);
// 输出: {Math=95, English=88}
// 3. 按列切片获取 (column - 极度便捷的转置提取)
System.out.println("\n--- 3. 按列提取数据 (column) ---");
Map<String, Integer> mathScores = scoreTable.column("Math");
System.out.println("所有学生的 Math 成绩 Map: " + mathScores);
// 输出: {Alice=95, Bob=80, Charlie=100}
// 4. 遍历整个 Table (cellSet)
System.out.println("\n--- 4. 遍历所有单元格 (cellSet) ---");
Set<Table.Cell<String, String, Integer>> cells = scoreTable.cellSet();
for (Table.Cell<String, String, Integer> cell : cells) {
System.out.println("学生: " + cell.getRowKey()
+ ", 学科: " + cell.getColumnKey()
+ " -> 成绩: " + cell.getValue());
}
// 5. 动态视图同步修改 (Live View 特性)
System.out.println("\n--- 5. 动态视图修改同步 ---");
// 对 column("Math") 返回的 Map 进行修改,原 Table 会同步改变
Map<String, Integer> mathMap = scoreTable.column("Math");
mathMap.put("David", 85); // 相当于执行了 scoreTable.put("David", "Math", 85)
System.out.println("通过 Math 视图新增 David 成绩后,原 Table 的 David 成绩: "
+ scoreTable.get("David", "Math")); // 输出: 85
// 6. 元素移除
scoreTable.remove("Bob", "English");
System.out.println("移除 Bob 的 English 成绩后 Table 大小: " + scoreTable.size()); // 输出: 5 (包含新加的 David)
}
}
--- 1. 单点查找与基础信息 ---
Alice 的 Math 成绩: 95
Charlie 的 English 成绩: null
单元格总数 (size): 5
所有学生 (rowKeySet): [Alice, Bob, Charlie]
所有学科 (columnKeySet): [Math, English]
--- 2. 按行提取数据 (row) ---
Alice 的所有学科成绩 Map: {Math=95, English=88}
--- 3. 按列提取数据 (column) ---
所有学生的 Math 成绩 Map: {Alice=95, Bob=80, Charlie=100}
--- 4. 遍历所有单元格 (cellSet) ---
学生: Alice, 学科: Math -> 成绩: 95
学生: Alice, 学科: English -> 成绩: 88
学生: Bob, 学科: Math -> 成绩: 80
学生: Bob, 学科: English -> 成绩: 92
学生: Charlie, 学科: Math -> 成绩: 100
--- 5. 动态视图修改同步 ---
通过 Math 视图新增 David 成绩后,原 Table 的 David 成绩: 85
移除 Bob 的 English 成绩后 Table 大小: 5
4.3、注意事项
- row() 与 column() 的“动态视图(Live View)”特性:
通过 table.row(rowKey) 或 table.column(columnKey) 返回的 Map 都是原 Table 的实时视图。对其进行 put()、remove() 或 clear(),会同步作用到原 Table 上。 - null 值处理:
- HashBasedTable 与 TreeBasedTable 的行键(RowKey)和列键(ColumnKey)允许为 null,但值(Value)不允许为 null。如果传入 null 值,会抛出 NullPointerException。
- 如果某个坐标上没有值,get(R, C) 会直接返回 null。
- 矩阵计算/转置选型:
- 需要快速横向检索(按行) 或 纵向检索(按列) 时,直接使用 row() 或 column() 即可实现 O ( 1 ) O(1) O(1) 的视图获取,免去了手动双层循环过滤的麻烦。
&spm=1001.2101.3001.5002&articleId=164293417&d=1&t=3&u=8534226a8d2c41beb1403b3745c5a014)
2459

被折叠的 条评论
为什么被折叠?



