Java使用映射器获取具有属性的Set的最大值



我有个问题。我正在尝试创建一个函数,该函数返回Set<Measurement>中给定属性的最大值。这就是我现在的功能:

public Map<Integer,Double> annualMaximumTrend(Function<Measurement,Double> mapper) {
Map<Integer, Double> maximumValue = new LinkedHashMap<>();
int startYear = determineStartYear();
for (int year = startYear; year <= LocalDate.now().getYear(); year++) {
LocalDate startDate = LocalDate.of(year - 1, 12, 31);
LocalDate endDate = LocalDate.of(year + 1, 1, 1);
Set<Measurement> stationAverages = new HashSet<>();
for (Station station : stations.values()) {
stationAverages.addAll(station
.getMeasurements()
.stream()
.filter(m -> m.getDate().isAfter(startDate) &&
m.getDate().isBefore(endDate) &&
!Double.isNaN(mapper.apply(m))
).collect(Collectors.toSet()));
}
OptionalDouble maximum = stationAverages.stream().mapToDouble(Measurement::getMaxTemperature).max();
if (maximum.isPresent())
maximumValue.put(year, maximum.getAsDouble());
}
return maximumValue;
}

但我需要确定不同的maximum,因为我需要在某个地方使用mapper.apply(),而不是Measurement::getMaxTemperature,因为映射器决定我想要哪个属性的最大值。我需要如何使用映射器?

您可以使用lambda:

stationAverages.stream().mapToDouble(m -> mapper.apply(m)).max();

如果mapperToDoubleFunction<Measurement>,则可以直接将其传递给mapToDouble()。由于它不是,您需要在另一个函数/lambda中调用它,例如mapToDouble(m -> mapper.apply(m))

或者,使用map(mapper).max(Comparator.naturalOrder())获取Optional<Double>

然而,请注意,您已经有了内部2个循环,其中一个是不必要的:

  1. 一个循环来构建集合
  2. 使用流的一个(隐藏(循环

您可以一次完成所有操作:

Optional<Double> max = stations.values().stream()
.flatMap(station -> station
.getMeasurements()
.stream()
.filter(m -> m.getDate().isAfter(startDate) &&
m.getDate().isBefore(endDate)
)
.map(mapper)
.filter(v -> !Double.isNaN(v))
)              
.max(Comparator.naturalOrder());

事实上,你甚至可以一次完成所有事情:

//stream the stations
Map<Integer, Double> maximumValue = stations.values().stream()
//flat map the stations to the measurements once
.flatMap(st -> st.getMeasurements().stream())
//filter measurements outside the time range
.filter(m -> m.getDate().getYear() >= startYear && m.getDate().getYear() <= endYear )
//filter measurements without a value
.filter(m -> !Double.isNaN(mapper.apply(m)))
//group measurements by year
.collect(Collectors.groupingBy(m->m.getDate().getYear(),                                    
//map the grouped measurements to the value
Collectors.mapping(mapper,  
//collect the max value and map it to a default value if it isn't present
//due to the filter above the default value should never be present
Collectors.collectingAndThen(
Collectors.maxBy(Comparator.naturalOrder()),
opt -> opt.orElse(Double.MIN_VALUE))
)));    

这应该可以提高更长时间和更多工作站的性能,因为您只在工作站上迭代一次,而不是多次。复杂性应该是O(s*m)而不是O(y*s*m)(y=年数,s=站数,m=每个站的测量次数(。

相比之下,这里有一个非流式功能等价物(由于不必打开Optional的包装,因此更简单(:

Map<Integer, Double> maximumValue = new LinkedHashMap<>();
for( Station station : stations.values() ) {
for( Measurement measurement : station.getMeasurements() ) {
//1st filter
if( !(measurement.getDate().getYear() >= startYear && measurement.getDate().getYear() <= endYear )) {
continue;
}
//2nd filter                
if( Double.isNaN(mapper.apply(measurement))) {
continue;
}

//put the value into the map or replace it if the other one is larger
maximumValue.merge(measurement.getDate().getYear(), mapper.apply(measurement), Math::max );
}
}

最新更新