Java 17 Stream API实现查找最接近今日日期的预测值
Java 17 无状态Stream实现近期预测值提取
需求说明
需要实现的业务逻辑规则如下:
- 方法入参接收
Collection<ForecastPerDate>类型集合,该数据结构为Java 17 record类型,定义为:public record ForecastPerDate(String date, Double value) {} - 过滤集合中日期大于等于当日的当日/未来条目
- 筛选出日期与当日差值最小(最接近今日)的条目,提取其
value字段 - 对提取到的数值先执行
floor向下取整,再执行round四舍五入,最终转换为Long类型返回 - 若集合为空、无符合过滤条件的条目或数值为空,打印对应业务日志后兜底返回
0
原有命令式实现
最初采用传统循环+可变变量的命令式范式实现,存在临时可变状态、额外Map存储的开销,代码如下:
@Override public Long getAvailabilityFromForecastData(final String fuCode, final String articleCode, final Collection<ForecastPerDate> forecasts) { if (forecasts == null || forecasts.isEmpty()) { log.info( "No forecasts received for FU {} articleCode {}, assuming 0!", fuCode, articleCode ); return 0L; } final long todayEpochDay = LocalDate.now().toEpochDay(); final Map<String, Double> forecastMap = new HashMap<>(); long smallestDiff = Integer.MAX_VALUE; String smallestDiffDate = null; for (final ForecastPerDate forecast : forecasts) { final long forecastEpochDay = LocalDate.parse(forecast.date()).toEpochDay(); final long diff = forecastEpochDay - todayEpochDay; if (diff >= 0 && diff < smallestDiff) { // 仅匹配当日及未来数据 smallestDiff = diff; smallestDiffDate = forecast.date(); forecastMap.put(forecast.date(), forecast.value()); } } if (smallestDiffDate != null) { final Double wantedForecastValue = forecastMap.get(smallestDiffDate); if (wantedForecastValue != null) { return availabilityAmountFormatter(wantedForecastValue); } } log.info( "Resorting to fallback for FU {} articleCode {}, 0 availability for article! Forecasts: {}", fuCode, articleCode, forecasts ); return 0L; } private Long availabilityAmountFormatter(final Double raw) { return Math.round(Math.floor(raw)); }
函数式无状态Stream实现
基于Java 17 Stream API完成优化,全程仅单次遍历流,无额外中间集合存储开销,无临时可变变量,完全符合函数式无状态的编码要求,代码如下:
private static long toEpochDay(final String date) { return LocalDate.parse(date).toEpochDay(); } @Override public Long getAvailabilityFromForecastData(final String fuCode, final String articleCode, final Collection<ForecastPerDate> forecasts) { final long today = LocalDate.now().toEpochDay(); final String fallbackMessage = "Resorting to fallback for FU {} articleCode {}," + " 0 availability for article! Forecasts: {}"; if (forecasts == null) { log.info(fallbackMessage, fuCode, articleCode, null); return 0L; } final Optional<ForecastPerDate> nearestValidForecast = forecasts.stream() // 若需包含当日数据,此处条件调整为 toEpochDay(fpd.date()) >= today .filter(fpd -> toEpochDay(fpd.date()) > today) .min(Comparator.comparingLong(fpd -> toEpochDay(fpd.date()) - today)); return nearestValidForecast .map(ForecastPerDate::value) .map(this::availabilityAmountFormatter) .orElseGet(() -> { log.info(fallbackMessage, fuCode, articleCode, forecasts); return 0L; }); } private Long availabilityAmountFormatter(final Double raw) { return Math.round(Math.floor(raw)); }
实现说明:直接使用Stream的
min终端操作,按日期与今日的差值做比较,天然适配「找最小差值元素」的需求,不需要手动实现reduce累加器逻辑,流框架本身已经做了单次遍历优化,不会产生额外迭代开销;同时用Optional的链式分支方法替代手动if判断,逻辑更紧凑,完全规避可变临时变量,也避免了手动调用Optional.get()可能产生的异常风险。
内容的提问来源于stack exchange,提问作者Josep Bigorra
相关产品推荐
相关产品推荐

