
本文介绍如何使用 java 8 stream api 和集合操作,对多级点分隔字符串(如 "sweden"、"sweden.stockholm")进行去重,并自动剔除所有被父级路径覆盖的子路径,最终保留最顶层的有效节点。
本文介绍如何使用 java 8 stream api 和集合操作,对多级点分隔字符串(如 "sweden"、"sweden.stockholm")进行去重,并自动剔除所有被父级路径覆盖的子路径,最终保留最顶层的有效节点。
在构建配置管理、权限路径、地理区域层级或资源路由等系统时,常需处理形如 "Root.Level1.Level2.Level3" 的点分隔字符串列表。这类数据具有天然的父子包含关系:"Sweden" 是 "Sweden.Stockholm" 的父级,而后者又是 "Sweden.Stockholm.Solna" 的父级。业务需求往往要求——仅保留“最粗粒度”的有效路径:即若父路径存在,则所有其子路径(无论几级)均应被剔除;同时原始列表中的重复项也需合并。
下面提供一种简洁、高效且符合函数式编程风格的 Java 8 解决方案,全程基于 Stream 与 Set 协同完成:
✅ 核心思路
-
去重:利用 HashSet 的唯一性,将原始 List
转为无重复 Set ; - 拓扑精简:遍历该集合的副本,对每个候选路径 str,从原集合中移除所有满足 v.startsWith(str + ".") 的 v(即 v 是 str 的直接或间接子路径);
- 保留结果:剩余元素即为不被任何其他元素包含的“顶层路径”。
⚠️ 注意:此逻辑隐含假设——合法路径中不含连续或结尾的点(如 "Sweden." 或 ".Stockholm"),且空字符串和纯 "." 不参与比较。若需更健壮校验,可在预处理阶段过滤非法项。
? 完整可运行示例代码
import java.util.*;
import java.util.stream.Collectors;
public class HierarchicalStringFilter {
public static List<String> retainTopLevelPaths(List<String> input) {
if (input == null || input.isEmpty()) {
return Collections.emptyList();
}
// Step 1: 去重 → 使用 HashSet 保证唯一性
Set<String> uniquePaths = input.stream()
.filter(Objects::nonNull) // 过滤 null(可选)
.filter(s -> !s.trim().isEmpty()) // 过滤空/空白字符串(推荐)
.collect(Collectors.toCollection(HashSet::new));
// Step 2: 创建副本用于遍历,避免 ConcurrentModificationException
Set<String> copy = new HashSet<>(uniquePaths);
// Step 3: 对每个路径 str,移除其所有子路径(v.startsWith(str + "."))
for (String str : copy) {
uniquePaths.removeIf(v ->
!v.equals(str) && v.startsWith(str + ".")
);
}
// 返回有序结果(可选:按字典序提升可读性)
return uniquePaths.stream()
.sorted()
.collect(Collectors.toList());
}
// 测试用例
public static void main(String[] args) {
List<String> input = Arrays.asList(
"Sweden", "Sweden",
"Sweden.Stockholm.Solna",
"America.Chicago", "America.Chicago",
"America.Chicago.Cicero"
);
List<String> result = retainTopLevelPaths(input);
System.out.println(result); // 输出: [America.Chicago, Sweden]
}
}? 关键细节说明
- v.startsWith(str + ".") 精确匹配父子关系:它确保 "America" 不会误判 "AmericaChicao"(因后者无 . 分隔),也自然排除了 str.equals(v) 的情况(通过 !v.equals(str) 显式排除);
- 使用 new HashSet(uniquePaths) 创建遍历副本,是安全修改原集合的必要实践;
- 若需支持空根(如 "" 表示 Root),只需微调判断逻辑(例如允许 str.isEmpty() 时用 v.contains(".") 判定是否为子项),但本例中未启用该语义;
- 时间复杂度为 O(n²·m)(n 为去重后路径数,m 为平均字符串长度),对千级以内数据性能优异;如需极致性能(万级+),可考虑构建 Trie 树优化前缀查询。
✅ 总结
该方案以声明式风格实现层级路径的“向上收敛”,无需递归或显式树结构,兼顾可读性、健壮性与 Java 8 特性表达力。开发者可直接集成至工具类,在微服务路径治理、前端菜单扁平化、多租户资源隔离等场景中快速落地。
立即学习“Java免费学习笔记(深入)”;










