一、代码示例
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class TimeGrouping {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
public static Map<String, List<String>> groupTimes(List<String> dates, String timeType) {
if ("day".equals(timeType)) {
return dates.stream().collect(Collectors.groupingBy(s -> s));
} else if ("week".equals(timeType)) {
return dates.stream()
.collect(Collectors.groupingBy(TimeGrouping::getWeek));
} else if ("month".equals(timeType)) {
return dates.stream()
.collect(Collectors.groupingBy(TimeGrouping::getMonth));
} else if ("year".equals(timeType)) {
return dates.stream()
.collect(Collectors.groupingBy(TimeGrouping::getYear));
} else {
throw new IllegalArgumentException("Unsupported time type: " + timeType);
}
}
private static String getWeek(String dateStr) {
LocalDate date = parseDate(dateStr);
int year = date.getYear();
int week = date.get(java.time.temporal.IsoFields.WEEK_OF_WEEK_BASED_YEAR);
return year + "-W" + String.format("%02d", week);
}
private static String getMonth(String dateStr) {
LocalDate date = parseDate(dateStr);
return date.getYear() + "-" + String.format("%02d", date.getMonthValue());
}
private static String getYear(String dateStr) {
LocalDate date = parseDate(dateStr);
return String.valueOf(date.getYear());
}
private static LocalDate parseDate(String dateStr) {
return LocalDate.parse(dateStr, FORMATTER);
}
// 将不同格式的日期字符串转换为LocalDate进行比较排序
private static LocalDate parseDateForSort(String dateStr) {
try {
return LocalDate.parse(dateStr, FORMATTER);
} catch (Exception e) {
// 处理格式为年 - 周的情况
if (dateStr.contains("-W")) {
int year = Integer.parseInt(dateStr.substring(0, 4));
int week = Integer.parseInt(dateStr.substring(6));
return LocalDate.of(year, 1, 1).plusWeeks(week - 1);
}
// 处理格式为年 - 月的情况
int year = Integer.parseInt(dateStr.substring(0, 4));
int month = Integer.parseInt(dateStr.substring(5));
return LocalDate.of(year, month, 1);
}
}
// 新增的方法,用于对分组后的Map按照时间先后顺序排序
public static Map<String, List<String>> sortResultByTime(Map<String, List<String>> result) {
return result.entrySet().stream()
.sorted((e1, e2) -> {
LocalDate date1 = parseDateForSort(e1.getKey());
LocalDate date2 = parseDateForSort(e2.getKey());
return date1.compareTo(date2);
})
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> v1,
LinkedHashMap::new
));
}
public static void main(String[] args) {
List<String> dateList = new ArrayList<>();
LocalDate startDate = LocalDate.parse("2024-11-16");
LocalDate endDate = LocalDate.parse("2024-12-20");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
while (!startDate.isAfter(endDate)) {
dateList.add(startDate.format(formatter));
startDate = startDate.plusDays(1);
}
String timeType = "xxx"; // 这里传入:day、week、month、year
Map<String, List<String>> result = groupTimes(dateList, timeType);
// 调用排序方法对结果进行排序
result = sortResultByTime(result);
result.forEach((k, v) -> System.out.println(k + " : " + v));
}
}
传入 year 时:
2024 : [2024-11-16, 2024-11-17, 2024-11-18, 2024-11-19, 2024-11-20, 2024-11-21, 2024-11-22, 2024-11-23, 2024-11-24, 2024-11-25, 2024-11-26, 2024-11-27, 2024-11-28, 2024-11-29, 2024-11-30, 2024-12-01, 2024-12-02, 2024-12-03, 2024-12-04, 2024-12-05, 2024-12-06, 2024-12-07, 2024-12-08, 2024-12-09, 2024-12-10, 2024-12-11, 2024-12-12, 2024-12-13, 2024-12-14, 2024-12-15, 2024-12-16, 2024-12-17, 2024-12-18, 2024-12-19, 2024-12-20]
传入 month 时:
2024-11 : [2024-11-16, 2024-11-17, 2024-11-18, 2024-11-19, 2024-11-20, 2024-11-21, 2024-11-22, 2024-11-23, 2024-11-24, 2024-11-25, 2024-11-26, 2024-11-27, 2024-11-28, 2024-11-29, 2024-11-30]
2024-12 : [2024-12-01, 2024-12-02, 2024-12-03, 2024-12-04, 2024-12-05, 2024-12-06, 2024-12-07, 2024-12-08, 2024-12-09, 2024-12-10, 2024-12-11, 2024-12-12, 2024-12-13, 2024-12-14, 2024-12-15, 2024-12-16, 2024-12-17, 2024-12-18, 2024-12-19, 2024-12-20]
传入 week 时:
2024-W46 : [2024-11-16, 2024-11-17]
2024-W47 : [2024-11-18, 2024-11-19, 2024-11-20, 2024-11-21, 2024-11-22, 2024-11-23, 2024-11-24]
2024-W48 : [2024-11-25, 2024-11-26, 2024-11-27, 2024-11-28, 2024-11-29, 2024-11-30, 2024-12-01]
2024-W49 : [2024-12-02, 2024-12-03, 2024-12-04, 2024-12-05, 2024-12-06, 2024-12-07, 2024-12-08]
2024-W50 : [2024-12-09, 2024-12-10, 2024-12-11, 2024-12-12, 2024-12-13, 2024-12-14, 2024-12-15]
2024-W51 : [2024-12-16, 2024-12-17, 2024-12-18, 2024-12-19, 2024-12-20]
传入 day 时:
2024-11-16 : [2024-11-16]
2024-11-17 : [2024-11-17]
2024-11-18 : [2024-11-18]
2024-11-19 : [2024-11-19]
2024-11-20 : [2024-11-20]
2024-11-21 : [2024-11-21]
2024-11-22 : [2024-11-22]
2024-11-23 : [2024-11-23]
2024-11-24 : [2024-11-24]
2024-11-25 : [2024-11-25]
2024-11-26 : [2024-11-26]
2024-11-27 : [2024-11-27]
2024-11-28 : [2024-11-28]
2024-11-29 : [2024-11-29]
2024-11-30 : [2024-11-30]
2024-12-01 : [2024-12-01]
2024-12-02 : [2024-12-02]
2024-12-03 : [2024-12-03]
2024-12-04 : [2024-12-04]
2024-12-05 : [2024-12-05]
2024-12-06 : [2024-12-06]
2024-12-07 : [2024-12-07]
2024-12-08 : [2024-12-08]
2024-12-09 : [2024-12-09]
2024-12-10 : [2024-12-10]
2024-12-11 : [2024-12-11]
2024-12-12 : [2024-12-12]
2024-12-13 : [2024-12-13]
2024-12-14 : [2024-12-14]
2024-12-15 : [2024-12-15]
2024-12-16 : [2024-12-16]
2024-12-17 : [2024-12-17]
2024-12-18 : [2024-12-18]
2024-12-19 : [2024-12-19]
2024-12-20 : [2024-12-20]
二、详细解读
以下是对上述Java代码的详细解读:
1. 整体功能概述
定义了一个名为TimeGrouping的类,主要功能是对给定的日期字符串列表按照不同的时间类型(天、周、月、年)进行分组,并提供了对分组结果按照时间先后顺序进行排序的功能。它使用了Java 8引入的java.time包来处理日期相关操作,以及利用Stream API进行便捷的集合处理。
2. 包导入与常量定义
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class TimeGrouping {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
- 开头导入了一系列必要的Java类,包括用于处理日期的
LocalDate、日期格式化的DateTimeFormatter以及用于操作集合的相关类。 - 定义了一个
private static final的DateTimeFormatter常量FORMATTER,用于将日期字符串按照yyyy-MM-dd的格式进行解析和格式化。
3. groupTimes方法
public static Map<String, List<String>> groupTimes(List<String> dates, String timeType) {
if ("day".equals(timeType)) {
return dates.stream().collect(Collectors.groupingBy(s -> s));
} else if ("week".equals(timeType)) {
return dates.stream()
.collect(Collectors.groupingBy(TimeGrouping::getWeek));
} else if ("month".equals(timeType)) {
return dates.stream()
.collect(Collectors.groupingBy(TimeGrouping::getMonth));
} else if ("year".equals(timeType)) {
return dates.stream()
.collect(Collectors.groupingBy(TimeGrouping::getYear));
} else {
throw new IllegalArgumentException("Unsupported time type: " + timeType);
}
}
-
这是一个静态方法,接受一个日期字符串列表
dates和一个表示时间类型的字符串timeType作为参数,并返回一个按照指定时间类型分组后的Map。 -
根据
timeType的值不同,使用Stream API的collect方法结合Collectors.groupingBy来对日期进行分组:- 当
timeType为"day"时,直接按照日期字符串本身进行分组,即每个不同的日期字符串是一个分组键,对应的值是包含该日期字符串的列表(实际上列表中只有这一个元素本身)。 - 当
timeType为"week"时,调用getWeek方法来提取每个日期所属的周信息(格式为年-W周数)作为分组键进行分组。 - 类似地,
"month"调用getMonth方法(提取年-月信息作为分组键),"year"调用getYear方法(提取年份作为分组键)来分别进行相应的分组操作。 - 如果传入的
timeType不支持,则抛出IllegalArgumentException异常。
- 当
4. 日期信息提取相关私有方法
private static String getWeek(String dateStr) {
LocalDate date = parseDate(dateStr);
int year = date.getYear();
int week = date.get(java.time.temporal.IsoFields.WEEK_OF_WEEK_BASED_YEAR);
return year + "-W" + String.format("%02d", week);
}
private static String getMonth(String dateStr) {
LocalDate date = parseDate(dateStr);
return date.getYear() + "-" + String.format("%02d", date.getMonthValue());
}
private static String getYear(String dateStr) {
LocalDate date = parseDate(dateStr);
return String.valueOf(date.getYear());
}
private static LocalDate parseDate(String dateStr) {
return LocalDate.parse(dateStr, FORMATTER);
}
getWeek方法:先通过parseDate方法将日期字符串解析为LocalDate对象,然后获取该日期对应的年份以及按照ISO标准的周数(一年中的第几周),最后将其格式化为年-W周数(周数占两位,不足两位前面补0)的字符串形式返回,用于按周分组时作为分组键。getMonth方法:同样先解析日期字符串为LocalDate,接着获取年份和月份,并格式化为年-月(月份占两位,不足两位前面补0)的字符串,用于按月分组的分组键。getYear方法:解析日期字符串得到LocalDate后,直接返回年份的字符串表示,用于按年分组的分组键。parseDate方法:利用前面定义的FORMATTER将输入的日期字符串按照yyyy-MM-dd格式解析成LocalDate对象,方便后续日期相关操作的使用。
5. 处理不同格式日期字符串解析用于排序的方法
private static LocalDate parseDateForSort(String dateStr) {
try {
return LocalDate.parse(dateStr, FORMATTER);
} catch (Exception e) {
// 处理格式为年 - 周的情况
if (dateStr.contains("-W")) {
int year = Integer.parseInt(dateStr.substring(0, 4));
int week = Integer.parseInt(dateStr.substring(6));
return LocalDate.of(year, 1, 1).plusWeeks(week - 1);
}
// 处理格式为年 - 月的情况
int year = Integer.parseInt(dateStr.substring(0, 4));
int month = Integer.parseInt(dateStr.substring(5));
return LocalDate.of(year, month, 1);
}
}
- 这个私有静态方法用于将不同格式的日期字符串转换为
LocalDate对象,以便进行后续的比较排序操作。 - 首先尝试按照标准的
yyyy-MM-dd格式解析日期字符串,如果解析成功则直接返回对应的LocalDate对象。 - 如果解析失败(可能是其他格式的日期表示),则进一步判断:
- 如果字符串包含
-W,则认为是年-W周数的格式,先提取年份和周数,然后以当年的1月1日为基础,通过plusWeeks方法往后推移相应周数得到对应的LocalDate对象。 - 如果不包含
-W,则认为是年-月的格式,提取年份和月份,通过LocalDate.of方法创建对应月份第一天的LocalDate对象。
- 如果字符串包含
6. sortResultByTime方法
public static Map<String, List<String>> sortResultByTime(Map<String, List<String>> result) {
return result.entrySet().stream()
.sorted((e1, e2) -> {
LocalDate date1 = parseDateForSort(e1.getKey());
LocalDate date2 = parseDateForSort(e2.getKey());
return date1.compareTo(date2);
})
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(v1, v2) -> v1,
LinkedHashMap::new
));
}
- 这是一个静态方法,用于对已经分组后的结果
Map(键为表示时间的字符串,值为对应时间下的日期字符串列表)按照时间先后顺序进行排序。 - 通过对
result的entrySet转换为Stream,在排序时,调用parseDateForSort方法将每个分组键(时间字符串)转换为LocalDate对象,然后根据LocalDate对象的比较结果(compareTo方法)来确定顺序。 - 最后使用
Collectors.toMap将排序后的Entry重新收集为一个Map,这里使用LinkedHashMap::new构造函数保证收集后的Map能保留排序后的顺序。
7. main方法
public static void main(String[] args) {
List<String> dateList = new ArrayList<>();
LocalDate startDate = LocalDate.parse("2024-12-16");
LocalDate endDate = LocalDate.parse("2024-12-20");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
while (!startDate.isAfter(endDate)) {
dateList.add(startDate.format(formatter));
startDate = startDate.plusDays(1);
}
String timeType = "xxx"; // 这里传入:day、week、month、year
Map<String, List<String>> result = groupTimes(dateList, timeType);
// 调用排序方法对结果进行排序
result = sortResultByTime(result);
result.forEach((k, v) -> System.out.println(k + " : " + v));
}
-
main方法是程序的入口点,用于演示TimeGrouping类中方法的使用:- 首先创建一个空的
ArrayList用于存放日期字符串。 - 定义了起始日期
startDate和结束日期endDate,并设置日期格式为yyyy-MM-dd。 - 通过循环,从起始日期开始,每天将日期格式化为字符串添加到
dateList中,直到达到结束日期。 - 定义了一个
timeType变量(示例中初始值为"xxx",实际使用时应传入"day"、"week"、"month"或"year"等合法的时间类型),调用groupTimes方法按照指定时间类型对dateList中的日期进行分组,得到分组结果result。 - 接着调用
sortResultByTime方法对分组结果result按照时间先后顺序进行排序。 - 最后通过
forEach方法遍历排序后的result,并打印出每个分组键(时间信息)及其对应的日期字符串列表内容。
- 首先创建一个空的
在实际使用时传入正确的timeType参数即可。