本文介绍如何使用 Java 8 的 Stream API 或传统循环,将形如 "20232,20221,..." 的年月编码字符串解析为 Map<String, String>,其中 key 为年份(如 "2023"),value 为该年所有月份(如 "1,2"),且月份严格升序、去重、无冗余逗号。
本文介绍如何使用 java 8 的 stream api 或传统循环,将形如 `"20232,20221,..."` 的年月编码字符串解析为 `map
在 Java 8 及以上版本中,处理此类结构化字符串(前4位为年、后若干位为月)并构建分组映射,推荐采用函数式风格与流式聚合相结合的方式,兼顾简洁性与可维护性。核心挑战在于:正确提取年/月、自动去重、保证月份升序、避免拼接时的尾部逗号。
✅ 推荐方案:Stream + Collectors(一行逻辑,清晰高效)
String str = "20232,20221,20222,20223,20224,20225,20226,20227,20228,20229,202210,202211,202212,20231";
// 第一步:按年分组,值为 TreeSet<Integer> → 自动去重 + 升序
Map<String, Set<Integer>> yearToMonths = Arrays.stream(str.split(","))
.collect(Collectors.groupingBy(
s -> s.substring(0, 4), // key: 年份(String)
Collectors.mapping(
s -> Integer.parseInt(s.substring(4)), // 提取月并转为 Integer
Collectors.toCollection(TreeSet::new) // 使用 TreeSet 保序去重
)
));
// 第二步:转换为 Map<String, String>,月份以逗号连接
Map<String, String> result = yearToMonths.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().stream()
.map(String::valueOf)
.collect(Collectors.joining(","))
));
// 输出结果
result.forEach((year, months) ->
System.out.println("[Key] : " + year + " [Value] : " + months));输出:
[Key] : 2023 [Value] : 1,2 [Key] : 2022 [Value] : 1,2,3,4,5,6,7,8,9,10,11,12
? 关键设计说明:
- TreeSet::new 确保月份自然排序(1,2,...,12)且自动去重;
- Collectors.mapping(...) 将原始字符串映射为整型月份,避免后续字符串比较歧义;
- Collectors.joining(",") 安全拼接,无需手动处理首尾逗号。
? 兼容方案:传统 for 循环(适合初学者或需调试场景)
若项目受限于可读性要求或团队对 Stream 不熟悉,以下显式循环方式同样健壮:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
立即学习“Java免费学习笔记(深入)”;
Map<String, Set<Integer>> yearToMonths = new HashMap<>();
String[] parts = str.split(",");
for (String part : parts) {
if (part.length() < 4) continue; // 防御性校验
String year = part.substring(0, 4);
int month = Integer.parseInt(part.substring(4));
yearToMonths.computeIfAbsent(year, k -> new TreeSet<>()).add(month);
}
// 构建最终结果 Map<String, String>
Map<String, String> result = new HashMap<>();
for (Map.Entry<String, Set<Integer>> entry : yearToMonths.entrySet()) {
StringJoiner joiner = new StringJoiner(",");
entry.getValue().forEach(m -> joiner.add(String.valueOf(m)));
result.put(entry.getKey(), joiner.toString());
}⚠️ 注意事项:
- 原始代码中 StringBuilder 在循环外复用导致所有年份共享同一实例,是典型 bug(造成值污染);
- substring(4) 可安全处理 "202210" 等多位月份,因 substring 截取至末尾;
- 若输入含非法格式(如 "2023" 无月份),建议增加 try-catch 或正则预校验(如 ^\d{4}\d+$)。
✅ 总结
无论选择 Stream 流式链式调用,还是传统迭代,核心逻辑一致:先按年分组 → 提取并标准化月份 → 排序去重 → 格式化输出。Stream 方案更符合现代 Java 实践,代码紧凑、意图明确;传统方案则便于逐行断点调试。二者均应优先使用 TreeSet 而非 ArrayList + Collections.sort(),以兼顾性能与语义清晰性。

















