Java根据时间范围判断有效数据

Java determine valid data depending on a time range

确定指定日期有效属性值的最佳方法是什么?

table 看起来像这样:

Attribute name Value valid from
attr1 41 01.01.2020
attr1 41,123 02.02.2021
attr1 41,456 02.02.2023

例如。如果日期是 13.06.2021,则有效值应为 41,123

第二种情况是当多一行可用时:

Attribute name Value valid from valid to
attr1 41 01.01.2020 01.01.2100
attr1 41,123 02.02.2021 02.04.2021
attr1 41,456 02.02.2023 01.01.2100

在这种情况下,有效值应为 41

由于您只提供了数据而没有提供代码,我们假设您有一个包含 Attribute 个元素的列表,如下所示(简化):

class Attribute {
  String name;
  double value;
  LocalDate validFrom;
  LocalDate validTo;
}

理想情况下,您可以为每个按 validFrom 排序的属性名称维护一个树图,例如Map<String, SortedMap<LocalDate, Attribute>>.

然后获取属性名称的排序映射,根据相关日期获取尾部或头部映射并迭代。假设您已按 validFrom 降序对地图进行排序,它可能如下所示:

Map<String, SortedMap<LocalDate, Attribute>> attribs = ....;

LocalDate targetDate = LocalDate.now();


//get the map for "attr1" or an empty map if there isn't one
Iterator<Attribute> itr = attribs.getOrDefault("attr1", Collections.emptySortedMap())
       .tailMap( targetDate )
       .values()
       .iterator();

//since we have descending order we're first checking the entries that have become valid last
//so just continue until you hit one that hasn't become invalid already or you run out of entries
while(itr.hasNext()) {
  Attribute attr = itr.next();
  if(!attr.validTo.isBefore(targetDate )) {
    return attr.value;
  }
}  

//no value found, could also return an empty Optional
return null;

如果您没有这样的地图,也可以使用流来完成,但对于重复查找来说效率会更低:

List<Attribute> attribs = ...;

LocalDate targetDate = LocalDate.now();

OptionalDouble value = 
       attribs.stream()
              .filter(a -> a.name.equals("attrib1")) //remove all other attribs
              .filter(a -> !a.validFrom.isAfter(targetDate)) //remove all that not yet valid
              .filter(a -> !a.validTo.isBefore(targetDate)) //remove all that aren't valid anymore
              .sorted(Comparator.comparing(a -> a.validFrom).reverseOrder()) //sort by validFrom in descending order to get the latest
              .mapToDouble(a -> a.value) //extract the value
              .findFirst(); //pick the first value if one exists