我有个问题。我正在尝试创建一个函数,该函数返回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();
如果mapper
是ToDoubleFunction<Measurement>
,则可以直接将其传递给mapToDouble()
。由于它不是,您需要在另一个函数/lambda中调用它,例如mapToDouble(m -> mapper.apply(m))
。
或者,使用map(mapper).max(Comparator.naturalOrder())
获取Optional<Double>
。
然而,请注意,您已经有了内部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 );
}
}