问题描述
我有一个 Country 类,并从 .csv 文件中读取了数据,该文件包含许多国家名称、它们所在的地区、每个国家的人口、地区等,并将其存储在 ArrayList 中。我主要使用 java 集合框架进行数据分析,并希望找到每个区域的总人口和平均人口。
我认为使用 HashMap 是最好的,但我不知道如何解决这个问题,因为我以前从未以任何复杂的方式或对象使用过。我也知道我必须将 int 的数据类型更改为总人口的 long。
public class Country {
private String name;
private String region;
private int population;
private int area;
private double density;
/**
* Default constructor
*/
public Country() {
}
/**
* Creates a country with all args
*
* @param name
* @param region
* @param population
* @param area
* @param density
*/
public Country(String name,String region,int population,int area,double density) {
super();
this.name = name;
this.region = region;
this.population = population;
this.area = area;
this.density = density;
}
/**
* @return the region
*/
public String getRegion() {
return region;
}
/**
* @param region the region to set
*/
public void setRegion(String region) {
this.region = region;
}
/**
* @return the population
*/
public int getPopulation() {
return population;
}
/**
* @param population the population to set
*/
public void setPopulation(int population) {
this.population = population;
}
public static void totalPopulationByRegion(Collection<Country> countries) {
Map<String,Integer> map = new HashMap<String,Integer>();
int total = 0;
for (Country country : countries) {
if (map.containsKey(country.getRegion())) {
map.put(country.getRegion(),total);
total+=country.getPopulation();
} else
map.put(country.getRegion(),total);
}
for (Map.Entry m : map.entrySet()) {
System.out.println(m.getKey() + " " + m.getValue());
}
}
从控制台上的输出中,我意识到我的数学逻辑在这方面完全错误,甚至考虑到我没有处理过大而无法存储为 int 的数字这一事实。我没有得到我想要的密钥的重复项,我只是不知道如何获得映射到每个区域的人口的累计总数。对此的任何帮助将不胜感激。
Near east 41843152
Asia -478957430
Europe -7912568
Africa 54079957
Latin amer. & carib 17926472
northern america -35219702
Baltics -1102504495
Oceania -616300040
来自 csv 文件的示例:
Country,Region,Population,Area (sq. mi.)
Afghanistan,ASIA,31056997,647500
Albania,EASTERN EUROPE,3581655,28748
Algeria,norTHERN AFRICA,32930091,2381740
American Samoa,OCEANIA,57794,199
Andorra,WESTERN EUROPE,71201,468
Angola,SUB-SAHaraN AFRICA,12127071,1246700
Anguilla,LATIN AMER. & CARIB,13477,102
Antigua & Barbuda,69108,443
Argentina,39921833,2766890
解决方法
如果您只想将区域与其总人口分组,那么您需要稍微修改您的代码。变量 total
应在您的 for
循环内声明,并应使用国家/地区的人口进行初始化。
public static void totalPopulationByRegion(Collection<Country> countries) {
Map</*Region*/ String,/*Population*/ Long> map = new HashMap<>();
for (Country country : countries) {
long total = country.getPopulation();
if (map.containsKey(country.getRegion())) {
total+=country.getPopulation();
}
map.put(country.getRegion(),total);
}
for (Map.Entry m : map.entrySet()) {
System.out.println(m.getKey() + " " + m.getValue());
}
}
但是,如果您希望对数据有更多的处理,那么如果您按区域和 Country
本身分组并缓存它以备将来使用这样的东西会更容易:
Map<String,List<Country>> groupData(Collection<Country> countries) {
Map</*Region*/String,List<Country>> map = new HashMap<>();
for (Country country : countries) {
List<Country> regionCountries = new ArrayList<>();
if (map.containsKey(country.getRegion())) {
regionCountries = map.get(country.getRegion());
}
regionCountries.add(country);
map.put(country.getRegion(),regionCountries);
}
return map;
}
然后此 data
可用于汇总每个区域的总人口和平均人口,如下所示(为方便起见,我使用的是 Java 8 Stream API):
Map<String,Integer> getTotalPopulationPerRegion(Map<String,List<Country>> data) {
Map<String,Integer> result = data.entrySet()
.stream()
.collect(Collectors.toMap(entry -> entry.getKey(),entry -> entry.getValue().stream().mapToInt(country -> country.getPopulation()).sum()));
return result;
}
Map<String,Double> getAveragePopulationPerRegion(Map<String,Double> result = data.entrySet()
.stream()
.collect(Collectors.toMap(entry -> entry.getKey(),entry -> entry.getValue().stream().mapToDouble(country -> country.getPopulation()).average().orElse(Double.NaN)));
return result;
}
,
假设你已经在你的country类中将population类型从int改为long
public static class Country {
private String name;
private String region;
private long population;
...
}
以下是实现您所需要的一些方法:
public static void totalPopulationByRegion(Collection<Country> countries) {
Map<String,Long> map = new HashMap<>();
for (Country country : countries) {
if (map.containsKey(country.getRegion())) {
//if the map contains the region get the value and add the population of current country
map.put(country.getRegion(),map.get(country.getRegion()) + country.getPopulation());
} else{
//else just put region of current country and population into the map
map.put(country.getRegion(),country.getPopulation());
}
}
for (Map.Entry m : map.entrySet()) {
System.out.println(m.getKey() + " " + m.getValue());
}
}
如果您使用的是 Java 8 或更高版本,可以使用 Map#computeIfPresent
和 Map#computeIfAbsent
缩短上述内容并避免 if else 块
public static void totalPopulationByRegion2(Collection<Country> countries) {
Map<String,Long> map = new HashMap<>();
for (Country country : countries) {
map.computeIfPresent(country.getRegion(),(reg,pop)-> pop + country.getPopulation());
map.computeIfAbsent(country.getRegion(),reg -> country.getPopulation());
}
for (Map.Entry m : map.entrySet()) {
System.out.println(m.getKey() + " " + m.getValue());
}
}
使用流 API,创建地图的任务可以使用 Collectors#groupingBy
和 Collectors#summingLong
public static void totalPopulationByRegion3(Collection<Country> countries) {
Map<String,Long> map =
countries.stream()
.collect(Collectors.groupingBy(Country::getRegion,Collectors.summingLong(Country::getPopulation)));
for (Map.Entry m : map.entrySet()) {
System.out.println(m.getKey() + " " + m.getValue());
}
}