MultiValueMap
# 功能介绍
MultiValueMap是Hutool中定义的多值Map接口,它扩展了普通Map的功能,允许一个键对应多个值。与普通Map不同,MultiValueMap的put方法不会覆盖已有键的值,而是将新值添加到该键对应的值集合中。这种特性使其非常适合处理一个键需要关联多个值的场景。
# 主要特性
- 支持一个键对应多个值
- 提供丰富的方法来操作多值映射
- 支持获取单个值或值集合
- 支持添加、删除、修改值
- 支持转换为普通Map
- 支持遍历所有键值对
- 提供多种实现类(ListValueMap、SetValueMap等)
- 接口设计简洁,易于扩展
# 适用场景
- 处理HTTP请求参数(同一个参数名可能有多个值)
- 处理表单提交(多个复选框可能提交同一个字段名)
- 构建多级索引
- 实现标签系统(一个内容可以有多个标签)
- 处理多对多关系数据
- 任何需要一个键关联多个值的场景
# 使用示例
# 示例1:基本用法
// 创建MultiValueMap实例(使用ListValueMap实现)
MultiValueMap<String, String> multiValueMap = new ListValueMap<>();
// 添加键值对,同一个键可以添加多个值
multiValueMap.put("tag", "java");
multiValueMap.put("tag", "tool");
multiValueMap.put("tag", "util");
multiValueMap.put("author", "looly");
// 获取单个值(返回第一个值)
String firstTag = multiValueMap.getFirst("tag");
// 输出: java
// 获取值集合
List<String> tags = multiValueMap.get("tag");
// 输出: [java, tool, util]
// 获取所有值
Collection<List<String>> allValues = multiValueMap.values();
// 遍历所有键值对
multiValueMap.forEach((key, values) -> {
Console.log(key + ": " + values);
});
// 输出:
// tag: [java, tool, util]
// author: [looly]
# 示例2:添加和删除值
MultiValueMap<String, String> multiValueMap = new ListValueMap<>();
multiValueMap.put("tag", "java");
multiValueMap.put("tag", "tool");
multiValueMap.put("tag", "util");
// 添加单个值
multiValueMap.add("tag", "library");
// 此时tag的值集合: [java, tool, util, library]
// 添加多个值
multiValueMap.addAll("tag", ListUtil.of("framework", "opensource"));
// 此时tag的值集合: [java, tool, util, library, framework, opensource]
// 删除指定键的指定值
multiValueMap.remove("tag", "tool");
// 此时tag的值集合: [java, util, library, framework, opensource]
// 删除指定键的所有值
multiValueMap.removeAll("tag");
// 此时tag的值集合: []
# 示例3:转换为普通Map
MultiValueMap<String, String> multiValueMap = new ListValueMap<>();
multiValueMap.put("tag", "java");
multiValueMap.put("tag", "tool");
multiValueMap.put("author", "looly");
// 转换为普通Map,值为列表
Map<String, List<String>> normalMap = multiValueMap.toMap();
// 转换为普通Map,值为第一个值
Map<String, String> firstValueMap = multiValueMap.toMap(false);
// 输出
Console.log(normalMap);
// 输出: {tag=[java, tool], author=[looly]}
Console.log(firstValueMap);
// 输出: {tag=java, author=looly}
# 示例4:遍历所有键值对
MultiValueMap<String, String> multiValueMap = new ListValueMap<>();
multiValueMap.put("tag", "java");
multiValueMap.put("tag", "tool");
multiValueMap.put("author", "looly");
// 遍历所有键值对(包括每个值)
multiValueMap.forEachEntry((key, value) -> {
Console.log(key + "=" + value);
});
// 输出:
// tag=java
// tag=tool
// author=looly
# 注意事项
- MultiValueMap是一个接口,需要使用具体的实现类(如ListValueMap、SetValueMap)来创建实例
- 不同的实现类有不同的特性:
- ListValueMap:值以List形式存储,保持插入顺序,允许重复值
- SetValueMap:值以Set形式存储,不允许重复值,无序
get()方法返回的是值的集合,而不是单个值getFirst()方法返回键对应的第一个值put()方法不会覆盖已有值,而是添加到值集合中- 大多数实现类是线程不安全的,在多线程环境下使用需要额外同步
- 支持null键和null值,但不同实现类对null值的处理可能不同
# 方法列表
| 方法名 | 描述 | 参数 | 返回值 |
|---|---|---|---|
| put(K key, V value) | 添加键值对,将值添加到值集合中 | key: 键, value: 值 | List |
| add(K key, V value) | 添加单个值到指定键 | key: 键, value: 值 | void |
| addAll(K key, Collection | 添加多个值到指定键 | key: 键, values: 值集合 | void |
| addAll(MultiValueMap<? extends K, ? extends V> map) | 添加另一个MultiValueMap的所有键值对 | map: 另一个MultiValueMap | void |
| get(K key) | 获取键对应的值集合 | key: 键 | List |
| getFirst(K key) | 获取键对应的第一个值 | key: 键 | V |
| remove(K key, V value) | 删除键对应的指定值 | key: 键, value: 值 | boolean |
| removeAll(K key) | 删除键对应的所有值 | key: 键 | List |
| clear() | 清空所有键值对 | 无 | void |
| size() | 获取键值对总数(每个值算一个键值对) | 无 | int |
| keySize() | 获取不同键的数量 | 无 | int |
| containsKey(K key) | 判断是否包含指定键 | key: 键 | boolean |
| containsValue(K key, V value) | 判断键是否包含指定值 | key: 键, value: 值 | boolean |
| keySet() | 获取所有键的集合 | 无 | Set |
| values() | 获取所有值集合的集合 | 无 | Collection<List |
| entries() | 获取所有键值对的迭代器 | 无 | Iterator<Map.Entry<K, V>> |
| forEach(BiConsumer<? super K, ? super List | 遍历所有键值集合对 | action: 遍历操作 | void |
| forEachEntry(BiConsumer<? super K, ? super V> action) | 遍历所有键值对(包括每个值) | action: 遍历操作 | void |
| toMap() | 转换为普通Map,值为列表 | 无 | Map<K, List |
| toMap(boolean singleValue) | 转换为普通Map,singleValue为true时只取第一个值 | singleValue: 是否只取第一个值 | Map<K, ?> |