
本文介绍如何将形如 "caso.responsavel.dadosPessoais.nome" 的点号分隔字符串列表,递归构建成嵌套的 `Map
在 Java 开发中,常需将扁平化的路径式字符串(如数据库字段映射、JSON Schema 路径或 DTO 层级属性)转化为直观的树状结构。例如,将 ["caso.id", "caso.responsavel.dadosPessoais.nome"] 自动组织为:
caso
id
responsavel
dadosPessoais
nome这无法通过单层 Collectors.groupingBy() 实现,而需结合递归建模 + 动态嵌套 Map。下面提供一套简洁、可复用、类型安全的解决方案。
✅ 核心思路
- 将每个字符串按 "\\." 拆分为路径节点数组(如 "caso.responsavel.nome" → ["caso", "responsavel", "nome"]);
- 使用递归方法 fill(Map
, String[] nodes, int index) 逐层插入: - 若当前节点(nodes[i])尚不存在,则新建子 HashMap 并挂载;
- 若已存在且为 Map,则递归进入该子 Map 继续处理后续节点;
- 到达末尾(i == nodes.length)时终止,无需额外存储值(仅建结构);
- 最终得到一个 Map
,其 value 可能是 Map (中间节点)或 null(叶子节点占位,实际可扩展为存储元数据)。
✅ 完整可运行代码
import java.util.*;
public class NestedGrouping {
public static void main(String[] args) {
String[] array = {
"caso.id",
"caso.unidadeDoCaso.id",
"caso.etiqueta",
"caso.sigiloso",
"caso.idPecaSegredoJustica",
"caso.numeroAno",
"caso.numero",
"caso.competencia.id",
"caso.competencia.ativo",
"caso.competencia.nome",
"caso.responsavel.id",
"caso.responsavel.dadosPessoais.nome",
"caso.escrivao.id",
"caso.escrivao.dadosPessoais.nome"
};
Map root = new HashMap<>();
for (String path : array) {
String[] nodes = path.split("\\.");
fill(root, nodes, 0);
}
print(root, "");
}
// 递归构建嵌套 Map 结构
public static void fill(Map map, String[] nodes, int i) {
if (i >= nodes.length) return;
String key = nodes[i];
Object existing = map.get(key);
if (existing == null) {
// 创建新子 Map 并挂载
Map child = new HashMap<>();
map.put(key, child);
fill(child, nodes, i + 1);
} else if (existing instanceof Map) {
// 已存在子 Map,继续递归
@SuppressWarnings("unchecked")
Map child = (Map) existing;
fill(child, nodes, i + 1);
}
// ⚠️ 注意:若 existing 非 null 且非 Map(如冲突路径),此处可抛异常或忽略(本例视为合法结构)
}
// 递归打印,带缩进层次
public static void print(Map map, String indent) {
for (String key : map.keySet()) {
System.out.println(indent + key);
Object value = map.get(key);
if (value instanceof Map) {
print((Map) value, indent + " ");
}
}
}
} ✅ 输出效果(节选)
caso
id
unidadeDoCaso
id
etiqueta
sigiloso
idPecaSegredoJustica
numeroAno
numero
competencia
id
ativo
nome
responsavel
id
dadosPessoais
nome
escrivao
id
dadosPessoais
nome⚠️ 注意事项与优化建议
-
类型安全性:Object 作为 value 类型虽灵活,但建议封装为泛型工具类(如 NestedPathTree
)或使用 Map 显式建模; - 重复路径处理:当前逻辑对重复路径(如两次 "caso.id")静默忽略;如需校验,可在 fill() 中添加 if (i == nodes.length - 1 && existing != null) 报警;
- 空节点防护:split("\\.") 可能产生空字符串(如 "a..b"),建议预处理:Arrays.stream(nodes).filter(s -> !s.isEmpty()).toArray(String[]::new);
- Stream 替代写法(非必须):虽然问题提到 Stream,但递归构建本质是副作用操作;若坚持函数式风格,可用 Arrays.stream(array).forEach(...) 替代 for-loop,语义等价;
- 生产环境增强:可扩展支持 JSON 序列化(如 Jackson 的 @JsonAnyGetter)、路径查询(get("caso.responsavel.dadosPessoais"))、或导出为 YAML/TreeModel。
此方案轻量、无外部依赖、逻辑清晰,适用于权限字段过滤、动态表单生成、API 响应结构推导等典型场景。
立即学习“Java免费学习笔记(深入)”;










