在批量处理场景(如解析 100 万条日志的时间戳、计算 100 万订单的超时时间)中,低效的日期计算会导致:
CPU 密集型开销:频繁的日期加减、时区转换涉及复杂的历法逻辑(如月份天数、闰年),消耗大量 CPU。
对象膨胀:每条数据创建一个LocalDateTime对象,导致内存占用激增,触发 GC。
下面给出了三种优化方案:
对于固定间隔(如 1 天、30 分钟),预定义毫秒数常量,避免重复计算:
public class TimeIntervalConstants {
public static final long MILLIS_PER_SECOND = 1000L;
public static final long MILLIS_PER_MINUTE = 60 * MILLIS_PER_SECOND;
public static final long MILLIS_PER_HOUR = 60 * MILLIS_PER_MINUTE;
public static final long MILLIS_PER_DAY = 24 * MILLIS_PER_HOUR;
public static final long MILLIS_PER_WEEK = 7 * MILLIS_PER_DAY;
}
// 使用时直接引用常量
long expireTime = createTime + TimeIntervalConstants.MILLIS_PER_DAY;示例代码:
package com.hxstrive.java_date_time.performance;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class BatchDateCalculation2 {
private static final long ONE_DAY_MILLIS = 24 * 60 * 60 * 1000L; // 1天的毫秒数
// 优化前
public LocalDateTime[] calculateExpireTimes(LocalDateTime[] createTimes) {
LocalDateTime[] result = new LocalDateTime[createTimes.length];
for (int i = 0; i < createTimes.length; i++) {
long createMillis = createTimes[i].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long expireMillis = createMillis + 24 * 60 * 60 * 1000L; // 看这里
result[i] = LocalDateTime.ofInstant(Instant.ofEpochMilli(expireMillis), ZoneId.systemDefault());
}
return result;
}
// 优化后
public LocalDateTime[] calculateExpireTimesOptimized(LocalDateTime[] createTimes) {
LocalDateTime[] result = new LocalDateTime[createTimes.length];
for (int i = 0; i < createTimes.length; i++) {
long createMillis = createTimes[i].atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
long expireMillis = createMillis + ONE_DAY_MILLIS; // 看这里
result[i] = LocalDateTime.ofInstant(Instant.ofEpochMilli(expireMillis), ZoneId.systemDefault());
}
return result;
}
public static void main(String[] args) {
LocalDateTime[] localDateTimes1 = new LocalDateTime[1000000];
LocalDateTime[] localDateTimes2 = new LocalDateTime[1000000];
long oneDay = 24 * 60 * 60 * 1000L;
for(int i = 0; i < localDateTimes1.length; i++) {
localDateTimes1[i] = LocalDateTime.ofInstant(Instant.ofEpochMilli(oneDay * i), ZoneId.systemDefault());
localDateTimes2[i] = LocalDateTime.ofInstant(Instant.ofEpochMilli(oneDay * i), ZoneId.systemDefault());
}
BatchDateCalculation2 demo = new BatchDateCalculation2();
// 优化前
long start = System.currentTimeMillis();
demo.calculateExpireTimes(localDateTimes1);
System.out.println("优化前耗时:" + (System.currentTimeMillis() - start));
// 优化后 —— 更慢了
long start2 = System.currentTimeMillis();
demo.calculateExpireTimesOptimized(localDateTimes2);
System.out.println("优化后耗时:" + (System.currentTimeMillis() - start2));
}
}运行结果:
优化前耗时:508 优化后耗时:409
解析大量字符串时,若所有字符串均为同一时区(如均为北京时间),可先将时区转换逻辑抽离,避免重复计算。例如:
package com.hxstrive.java_date_time.performance;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
public class BatchParsing {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final ZoneId ZONE = ZoneId.of("Asia/Shanghai");
// 优化前:每条数据单独处理时区
public List<Instant> parseToInstant(List<String> dateStrs) {
List<Instant> result = new ArrayList<>(dateStrs.size());
for (String str : dateStrs) {
// 每次解析都包含时区转换(重复计算)
LocalDateTime ldt = LocalDateTime.parse(str, FORMATTER);
result.add(ldt.atZone(ZONE).toInstant()); // 看这里
}
return result;
}
// 优化后:提前绑定时区到Formatter,减少转换步骤
public List<Instant> parseToInstantOptimized(List<String> dateStrs) {
// 关键:创建带有时区的Formatter,解析时直接得到Instant
DateTimeFormatter formatterWithZone = FORMATTER.withZone(ZONE); // 看这里
List<Instant> result = new ArrayList<>(dateStrs.size());
for (String str : dateStrs) {
// 直接解析为Instant,减少中间对象
result.add(Instant.from(formatterWithZone.parse(str)));
}
return result;
}
public static void main(String[] args) {
List<String> dateStrList = new ArrayList<>();
for(int i = 0; i < 1000000; i++) {
dateStrList.add(FORMATTER.format(LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault())));
}
BatchParsing demo = new BatchParsing();
// 优化前
long start = System.currentTimeMillis();
demo.parseToInstant(dateStrList);
System.out.println("优化前耗时:" + (System.currentTimeMillis() - start));
// 优化后
long start2 = System.currentTimeMillis();
demo.parseToInstantOptimized(dateStrList);
System.out.println("优化后耗时:" + (System.currentTimeMillis() - start2));
}
}运行结果:
优化前耗时:805 优化后耗时:617
对于超大数据量(如 1000 万+),可使用并行流(parallelStream)或线程池拆分任务,利用多核 CPU 提升效率。例如:
package com.hxstrive.java_date_time.performance;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class ParallelDateProcessing {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.withZone(ZoneId.of("Asia/Shanghai"));
// 普通解析大量时间字符串
public List<Instant> parse(List<String> dateStrs) {
List<Instant> retList = new ArrayList<>(dateStrs.size());
for(String dateStr : dateStrs) {
retList.add(Instant.from(FORMATTER.parse(dateStr)));
}
return retList;
}
// 并行解析大量时间字符串
public List<Instant> parseInParallel(List<String> dateStrs) {
// 使用并行流自动拆分任务到多个线程
return dateStrs.parallelStream()
.map(str -> Instant.from(FORMATTER.parse(str)))
.collect(Collectors.toList());
}
public static void main(String[] args) {
List<String> dateStrList = new ArrayList<>();
for(int i = 0; i < 1000000; i++) {
dateStrList.add(FORMATTER.format(LocalDateTime.ofInstant(Instant.now(), ZoneId.systemDefault())));
}
ParallelDateProcessing demo = new ParallelDateProcessing();
// 优化前
long start = System.currentTimeMillis();
demo.parse(dateStrList);
System.out.println("优化前耗时:" + (System.currentTimeMillis() - start));
// 优化后
long start2 = System.currentTimeMillis();
demo.parseInParallel(dateStrList);
System.out.println("优化后耗时:" + (System.currentTimeMillis() - start2));
}
}运行结果:
优化前耗时:838 优化后耗时:177