【问题标题】:Using HashMaps to map a total and average value to a key使用 HashMaps 将总值和平均值映射到键
【发布时间】:2021-06-19 00:52:54
【问题描述】:

我有一个 Country 类,并从 .csv 文件中读取数据,该文件包含许多国家名称、它们所在的地区、每个国家的人口、地区等,并将其存储在 ArrayList 中。我主要使用 java 集合框架进行数据分析,并希望找到每个区域的 total平均人口

我认为使用 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 的数字这一事实。我没有得到我想要的键的重复项,我只是不知道如何获得映射到每个区域的人口的累积总数。对此的任何帮助将不胜感激。

从 main 方法调用时得到的输出:


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 ,LATIN AMER. & CARIB    ,69108,443
Argentina ,LATIN AMER. & CARIB    ,39921833,2766890

【问题讨论】:

  • 你必须把计算出来的total放回MapInteger是不可变的)。另外我建议看一下IntSummaryStatisticsLongSummaryStatistics(如果在地图中使用而不是Integer,则无需写回)
  • 你认为你的else 块有什么作用?你为什么写那段代码?
  • 你的预期结果是什么?
  • 请分享您的 Country 类、您的 csv 文件的示例输入和预期输出。
  • 如果每个Country 都属于一个Region,您可以创建一个Map&lt;Region, List&lt;Country&gt;&gt; 并对其进行迭代。

标签: java foreach collections hashmap data-analysis


【解决方案1】:

如果您只想将区域与总人口进行分组,那么您需要稍微修改一下您的代码。变量 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, List<Country>> data) {
        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;
    }

【讨论】:

    【解决方案2】:

    假设您已经在您的国家/地区类中将人口类型从 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#computeIfPresentMap#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#groupingByCollectors#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());
        }
    }
    

    【讨论】:

    • 我将如何使用类似的方法来计算每个地区的平均人口?我尝试在 for each 中使用一个变量 instanceOfPop++,然后在 map.get(country.getRegion()) + country.getPopulation()) 中使用该变量;作为除以的数字,但我没有得到准确的计算。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-04
    • 1970-01-01
    • 1970-01-01
    • 2016-02-10
    • 2017-10-25
    相关资源
    最近更新 更多