Java 8 Streams多个分组依据
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 8 Streams多个分组依据相关的知识,希望对你有一定的参考价值。
我的温度记录是这样的
dt |AverageTemperature |AverageTemperatureUncertainty|City |Country |Latitude|Longitude
----------+-------------------+-----------------------------+-------+--------+--------+---------
1963-01-01|-5.417000000000002 |0.5 |Karachi|Pakistan|57.05N |10.33E
1963-02-01|-4.7650000000000015|0.328 |Karachi|Pakistan|57.05N |10.33E
1964-01-01|-5.417000000000002 |0.5 |Karachi|Pakistan|57.05N |10.33E
1964-02-01|-4.7650000000000015|0.328 |Karachi|Pakistan|57.05N |10.33E
1965-01-01|11.417000000000002 |0.5 |Karachi|Pakistan|57.05N |10.33E
1965-02-01|12.7650000000000015|0.328 |Karachi|Pakistan|57.05N |10.33E
我必须将其解析为POJO并根据以下问题陈述计算平均增量:
使用Streams API计算每个国家/地区的年平均温度增量。为了计算delta,1900年的平均温度将从1901年的平均温度中减去,以获得特定城市1900年至1901年的增量。所有这些增量的平均值是一个城市的年平均温度增量。一个国家所有城市的平均值是一个国家的平均值。
我的温和POJO看起来像是有吸气剂和二传手
public class Temperature {
private java.util.Date date;
private double averageTemperature;
private double averageTemperatureUncertainty;
private String city;
private String country;
private String latitude;
private String longitude;
}
我已经保留了一份温度列表,因为要使用流来解决这个问题。
要计算delta我试图使用以下流但我仍然无法计算实际的delta,因为我必须计算平均国家/地区delta,我已经对国家/地区,城市和日期进行了分组。
Map<String, Map<String, Map<Integer, Double>>> countriesMap = this.getTemperatures().stream()
.sorted(Comparator.comparing(Temperature::getDate))
.collect(Collectors.groupingBy(Temperature::getCountry,
Collectors.groupingBy(Temperature::getCity,
Collectors.groupingBy
(t -> {
Calendar calendar = Calendar.getInstance();
calendar.setTime(t.getDate());
return calendar.get(Calendar.YEAR);
},
Collectors.averagingDouble(Temperature::getAverageTemperature)))));
为了计算delta,我们必须计算Map<Integer, Double>
的差异。
为了计算差异,我提出了以下代码,但无法将以下代码与上面的代码连接起来
Stream.of(10d, 20d, 10d) //this is sample data that I that I get in `Map<Integer, Double>` of countriesMap
.map(new Function<Double, Optional<Double>>() {
Optional<Double> previousValue = Optional.empty();
@Override
public Optional<Double> apply(Double current) {
Optional<Double> value = previousValue.map(previous -> current - previous);
previousValue = Optional.of(current);
return value;
}
})
.filter(Optional::isPresent)
.map(Optional::get)
.forEach(System.out::println);
如何一次性使用流计算delta或如何在countriesMap
上执行流操作以计算delta和实现上述问题的声明。
为了将问题陈述减少到一个较小的块,你可以研究的另一种方法是解析year
ly温度并计算它们的delta,进一步average
ing它。对于你问题中内部Map<Integer, Double>
中Map
类型的所有值,都必须这样做。它看起来像:
Map<Integer, Double> unitOfWork = new HashMap<>(); // innermost map you've attained ('yearToAverageTemperature' map)
unitOfWork = unitOfWork.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
// the values sorted based on the year from a sorted map
List<Double> srtedValPerYear = new ArrayList<>(unitOfWork.values());
// average of deltas from the complete list
double avg = IntStream.range(0, srtedVal.size() - 1)
.mapToDouble(i -> (srtedVal.get(i + 1) - srtedVal.get(i)))
.average().orElse(Double.NaN);
进一步注意,这只是一个City
的<Year, AverageTemperature>
记录的平均值,你必须遍历所有City
键集,并且类似地为你所有的Country
键集详尽地找出这样的平均值。
进一步将这个工作单元移动到一个方法中,迭代完整的地图图,这可以完成为:
// The average of all cities in a country is the average of a country.
AtomicReference<Double> countryValAvg = new AtomicReference<>(0.0);
countriesMap.forEach((country, cityMap) -> {
// The average of all these deltas is the average annual temperature delta for a city.
AtomicReference<Double> cityAvgTemp = new AtomicReference<>((double) 0);
cityMap.forEach((city, yearMap) -> cityAvgTemp.set(cityAvgTemp.get() + averagePerCity(yearMap)));
double avgAnnualTempDeltaPerCity = cityAvgTemp.get() / cityMap.size();
countryValAvg.set(countryValAvg.get() + avgAnnualTempDeltaPerCity);
});
System.out.println(countryValAvg.get() / countriesMap.size());
其中averagePerCity
是遵循的方法
double averagePerCity(Map<Integer, Double> unitOfWork) {
unitOfWork = unitOfWork.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
List<Double> srtedVal = new ArrayList<>(unitOfWork.values());
return IntStream.range(0, srtedVal.size() - 1)
.mapToDouble(i -> (srtedVal.get(i + 1) - srtedVal.get(i)))
.average().orElse(Double.NaN);
}
注意:上面的代码可能缺少验证,它只是为了提供一个概念,即如何将完整的问题分解成更小的部分然后解决。
编辑1:哪个could be improved further as:
// The average of all cities in a country is the average of a country.
AtomicReference<Double> countryValAvg = new AtomicReference<>(0.0);
countriesMap.forEach((country, cityMap) -> {
// The average of all these deltas is the average annual temperature delta for a city.
double avgAnnualTempDeltaPerCity = cityMap.values()
.stream()
.mapToDouble(Quick::averagePerCity) // Quick is my class name
.average()
.orElse(Double.NaN);
countryValAvg.set(countryValAvg.get() + avgAnnualTempDeltaPerCity);
});
System.out.println(countryValAvg.get() / countriesMap.size());
编辑2:进一步
double avgAnnualTempDeltaPerCity = countriesMap.values().stream()
.mapToDouble(cityMap -> cityMap.values()
.stream()
.mapToDouble(Quick::averagePerCity) // Quick is my class name
.average()
.orElse(Double.NaN))
.average().orElse(Double.NaN);
以上是关于Java 8 Streams多个分组依据的主要内容,如果未能解决你的问题,请参考以下文章