在批量处理场景(如解析 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