最近在一个项目中要实现一个时间段合并的业务,即多个可能重复的时间段,合并成一个时间段(应该是取并集)
这里要感谢某技术交流群里的叫嗯*的小哥,给我提供了解决方案
public class EnhanceDataUtil extends DateUtil {
/**
* 日期合并 -Date
* 多个时间段 是有可能部分重合的 汇聚成一条时间线
* 比如8:00-9:00、8:30-10:30、10:40-11:10、10:36-11:00
* 最后输出:8:00-10:30、10:36-11:10
*
* @param sourceDateRanges 输入多个时间范围的时间段列表
* @return 合并压缩成只有开始和结束时间的时间段列表
*/
public static List<Date[]> amalgamateDate(List<Date[]> sourceDateRanges) {
// 防止入参被篡改 深拷贝
sourceDateRanges = JSONUtil.toBean(JSONUtil.toJsonStr(CollUtil.emptyIfNull(sourceDateRanges)), new TypeReference<List<Date[]>>() {
}, true);
// 返回值
List<Date[]> amalgamateList = new ArrayList<>();
// 数据处理
sourceDateRanges = sourceDateRanges.stream()
// 去掉空元素
.map(ArrayUtil::removeNull)
// 只保留元素个数大于1的
.filter(sourceDateRange -> ArrayUtil.length(sourceDateRange) > 1)
// 只保留start和end
.map(sourceDateRange -> new Date[]{
ArrayUtil.min(sourceDateRange),
ArrayUtil.max(sourceDateRange)
})
.map(sourceDateRange ->
Convert.convert(DateTime[].class, sourceDateRange))
// 排序
.sorted((o1, o2) -> CompareUtil.compare(o1[0], o2[0]))
.collect(Collectors.toList());
/**
* 时间段位置映射集 以源列表时间段的索引为key,合并到结果列表的索引为value
* sourceDateRangesIndex:amalgamateListIndex
*/
HashMap<Integer, Integer> indexMap = new HashMap<>();
// 添加首个元素
if (CollUtil.isNotEmpty(sourceDateRanges)) {
amalgamateList.add(CollUtil.getFirst(sourceDateRanges));
indexMap.put(0, 0);
}
/**
* 开始合并:
* amalgamateList能够确保是没有任何交集的时间段列表
* sourceDateRanges是按照时间段中的起始时间正序排列的列表,是有可能有交集的
* 上面的排序很重要,能够确保每次拿到最新的时间段数据 不会合并到amalgamateList前面的元素中,只能合并到最后一个时间段或者开启新的时间段
*/
for (int index = 1; index < sourceDateRanges.size(); index++) {
// 找到上一个合并好的时间段
Date[] amalgamateDateRange = amalgamateList.get(indexMap.get(index - 1));
// 当前待合并的时间段
Date[] currentIndexDataRange = sourceDateRanges.get(index);
if (isIn(currentIndexDataRange[0], amalgamateDateRange[0], amalgamateDateRange[1])) {
// 如果当前时间段的起始时间在上个合并好的时间段内 调整上个时间段的起始、结束点
amalgamateDateRange[0] = CollUtil.min(Arrays.asList(amalgamateDateRange[0], currentIndexDataRange[0]));
amalgamateDateRange[1] = CollUtil.max(Arrays.asList(amalgamateDateRange[1], currentIndexDataRange[1]));
// 未合并列表索引:合并到amalgamateList的索引
indexMap.put(index, indexMap.get(index - 1));
} else {
amalgamateList.add(currentIndexDataRange);
// 未合并列表索引:新增到amalgamateList的索引
indexMap.put(index, CollUtil.size(amalgamateList) - 1);
}
}
return amalgamateList;
}
/**
* 日期合并 - LocalDateTime
*
* @param sourceDateRanges
* @return
*/
public static List<LocalDateTime[]> amalgamateLocalDateTime(List<LocalDateTime[]> sourceDateRanges) {
ZoneId zoneId = ZoneId.systemDefault();
return amalgamateDate(
sourceDateRanges.stream().map(range ->
Arrays.stream(range)
.map(time ->
ObjectUtil.isEmpty(time) ? null : Date.from(time.atZone(zoneId).toInstant()))
.collect(Collectors.toList())
.toArray(new Date[0]))
.collect(Collectors.toList())
).stream().map(dateRange ->
Arrays.stream(dateRange)
.map(date ->
date.toInstant().atZone(zoneId).toLocalDateTime())
.collect(Collectors.toList())
.toArray(new LocalDateTime[0]))
.collect(Collectors.toList()
);
}
}