
本文介绍如何使用 Java 8 的 Stream API 和传统循环两种方式,将形如 "20232,20221,..." 的年月编码字符串解析为 Map,其中 key 为年份(如 "2023"),value 为该年所有月份(如 "1,2"),且月份严格按数字升序排列。
本文介绍如何使用 java 8 的 stream api 和传统循环两种方式,将形如 `"20232,20221,..."` 的年月编码字符串解析为 `map
在 Java 应用中,常需处理紧凑编码的时间字符串(如 "20232" 表示 2023 年 2 月)。本教程提供两种专业、健壮的实现方案:函数式流式处理(推荐) 和 清晰易懂的传统循环方式,均确保月份去重、升序排列,并避免常见陷阱(如 StringBuilder 累加导致的重复拼接、月份解析越界等)。
✅ 方案一:Java 8 Stream + Collectors(简洁高效)
利用 Collectors.groupingBy 分组、Collectors.mapping 提取并转换、TreeSet::new 自动排序,再通过二次收集生成最终字符串:
String str = "20232,20221,20222,20223,20224,20225,20226,20227,20228,20229,202210,202211,202212,20231";
// 第一步:构建 Map<string treeset>> —— 年份 → 升序月份集合
Map<string set>> yearToMonths = Arrays.stream(str.split(","))
.collect(Collectors.groupingBy(
s -> s.substring(0, 4), // key: 前4位为年份
Collectors.mapping(
s -> Integer.parseInt(s.substring(4)), // value: 后缀转为整数(自动处理"10"/"11"/"12")
Collectors.toCollection(TreeSet::new) // 自动去重+升序
)
));
// 第二步:转换为 Map<string string> —— 年份 → "1,2,3,..."
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));</string></string></string></string>
✅ 输出示例:
[Key] : 2023 [Value] : 1,2
[Key] : 2022 [Value] : 1,2,3,4,5,6,7,8,9,10,11,12
JavaStoryDevelop下载你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
✅ 方案二:传统 for 循环(新手友好,逻辑透明)
适合初学者理解执行流程,显式控制每一步,避免流式操作的抽象性:
String str = "20232,20221,20222,20223,20224,20225,20226,20227,20228,20229,202210,202211,202212,20231";
String[] parts = str.split(",");
Map<string set>> yearToMonths = new HashMap();
Map<string string> result = new HashMap();
for (String part : parts) {
if (part.length() new TreeSet()).add(month);
} catch (NumberFormatException e) {
System.err.println("Invalid month format in '" + part + "': " + e.getMessage());
continue;
}
}
// 构建最终字符串结果
for (Map.Entry<string set>> entry : yearToMonths.entrySet()) {
StringJoiner joiner = new StringJoiner(",");
for (int month : entry.getValue()) {
joiner.add(String.valueOf(month));
}
result.put(entry.getKey(), joiner.toString());
}</string></string></string>
⚠️ 关键注意事项:
- 使用 TreeSet 而非 HashSet 或 ArrayList,确保月份天然升序且无重复;
- substring(4) 可安全处理 "202210"(→ "10")、"20231"(→ "1"),无需预判位数;
- 添加 try-catch 防御性解析,避免 NumberFormatException;
- 推荐用 StringJoiner 替代 StringBuilder 拼接,语义更清晰、无尾逗号风险;
- computeIfAbsent 替代手动 containsKey 判断,代码更简洁。
✅ 总结
无论选择 Stream 还是传统循环,核心逻辑一致:按年分组 → 提取月份 → 排序去重 → 拼接字符串。Stream 方案更符合现代 Java 风格,代码精炼、可读性强;传统方案则便于调试与教学。实际项目中,建议优先采用 Stream + TreeSet 组合,兼顾性能、健壮性与可维护性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











