Guava——集合(一)

1、Multimap(一键多值映射)

com.google.common.collect.Multimap 是 Guava 提供的一对多映射集合。它解决了原生 Java 中 Map<K, List> 或 Map<K, Set> 维护繁琐的问题(如手动初始化集合、判空、移除元素等),将一个键对应多个值的逻辑封装为统一且简洁的接口。

核心实现类分类

Multimap 是一个接口,针对值的底层实现方式不同,Guava 提供了以下主要实现:

实现类值的存储类型 (get(key) 的返回值)允许重复值关联的原生结构
ArrayListMultimapList<V>Map<K, List<V>>
HashMultimapSet<V>Map<K, Set<V>>
LinkedListMultimapList<V>(双向链表,保持插入顺序)Map<K, LinkedList<V>>
TreeMultimapNavigableSet<V>(有序)TreeMap<K, TreeSet<V>>
LinkedHashMultimapSet<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 返回的集合后,原 MultimapAlice 课程: [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>。

核心实现类分类

实现类关联的原生结构说明
HashMultisetHashMap最常用,元素无序,基于哈希表实现
TreeMultisetTreeMap元素按自然顺序或指定的 Comparator 排序
LinkedHashMultisetLinkedHashMap保持元素的首次插入顺序
ConcurrentHashMultisetConcurrentHashMap线程安全的实现,适用于并发计数

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)booleanCAS 替换:仅当旧数量等于 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 必须为枚举)
EnumHashBiMapKey 为枚举,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 单元格映射、权限矩阵或多维度交叉索引时非常高效。

核心实现类分类

实现类关联的原生结构说明
HashBasedTableHashMap<R, HashMap<C, V>>最常用,行和列均基于哈希表实现,无序
TreeBasedTableTreeMap<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. 单点查找与基础信息 ---
AliceMath 成绩: 95
CharlieEnglish 成绩: 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 成绩后,原 TableDavid 成绩: 85
移除 BobEnglish 成绩后 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) 的视图获取,免去了手动双层循环过滤的麻烦。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值