【问题标题】:Put a sum as value in a hashmap将总和作为值放入哈希图中
【发布时间】:2020-03-13 12:14:37
【问题描述】:

我的两个列表list2filetemp 的长度相同,并且包含日期和温度。列表中的一些元素如下所示:

[1946-01-12, 1946-01-12, 1946-01-12, 1946-01-13, 1946-01-13, 1946-01-13, 1946-01-14, 1946-01-14, 1946-01-14]
[-1.3, 0.3, -2.8, -6.2, -4.7, -4.3, -1.5, -0.2, -0.4]

我想把它们放在一个哈希图中,日期为键,温度为值。由于我有与每个日期对应的多个温度值,因此我想将每天的温度值求和(或者如果可能的话计算每天的平均温度),以便地图中的每个日期对应于该日期的平均温度。我现在的问题是我的地图看起来像:

1946-01-12: -2.8
1946-01-13: -4.3
1946-01-14: -0.4

所以不是对日期 1946-01-14 求和例如 -1.5+(-0.2)+(-0.4),而是返回该日期的最后一个温度。有人知道怎么解决吗?

public Map<LocalDate, Double> avarageTemperatures(LocalDate dateFrom, LocalDate dateTo) {
    List<LocalDate> list2 = new ArrayList<>();
    for (Weather weather : weatherData) {
        list2.add(weather.getDateTime());
    }

    Map<LocalDate, Double> map = new HashMap<>();
    List<Double> filetemp = new ArrayList<>();
    for (Weather weather : weatherData) {
        filetemp.add(weather.getTemperature());
    }
    Double val= 0.0;
    for (int i=1; i<list2.size(); i++) {
        if(list2.get(i)==list2.get(i-1)) {
            val+= filetemp.get(i);
            map.put(list2.get(i), val);
        } else {
            val=filetemp.get(i);
            map.put(list2.get(i), val);
        }
    }

    Set<Map.Entry<LocalDate, Double>> entrySet = map.entrySet();
    for(Map.Entry<LocalDate,Double> entry : entrySet) {
        if(!entry.getKey().isAfter(dateTo) && !entry.getKey().isBefore(dateFrom)) {
            System.out.println(entry.getKey()+": "+(entry.getValue()));
        }
    }

    return map;
}

【问题讨论】:

  • 请正确格式化代码并使用语义名称,而不是entry : entrySet。它更难阅读,而且由于您的问题基本上是要求这里的人调试您的代码,因此您应该确保代码尽可能好。
  • 发布编译所需的代码。
  • 日期总是排序的吗?因为这可以使这个计算更容易一些
  • @Rogue 是的,总是排序的
  • 为什么要创建两个列表?有一个包含日期和相​​应温度的天气对象。

标签: java list loops hashmap average


【解决方案1】:

嗯,这是使用streams 并创建地图地图的一种方法。内部地图只保存sumaverage 温度。也可以使用 List,但地图允许符号索引以帮助保持直截了当。

它的工作原理如下:

  • 使用Collectors.teeing 方法创建两个流。第一个对日期进行频率计数。第二,增加临时工。两者最终都出现在以日期为键的地图中。
  • 然后通过将总和除以日期的频率计数来计算平均温度。这允许每个样本的日期数量在数量上有所不同,但仍然可以获得准确的平均值。
  • 温度总和和平均温度都存储为地图。
  • TreeMap 用于外部映射以按自然顺序对键进行排序。

    List<String> dates =
            List.of("1946-01-12", "1946-01-12", "1946-01-12",
                    "1946-01-13", "1946-01-13", "1946-01-13",
                    "1946-01-14", "1946-01-14", "1946-01-14");

    List<Double> temps = List.of(-1.3, 0.3, -2.8, -6.2, -4.7,
            -4.3, -1.5, -0.2, -0.4);

    Map<String,Map<String, Double>> data = IntStream
            .range(0,
                    dates.size())
            .boxed()
            .collect(Collectors.teeing(
                    Collectors.groupingBy(i -> dates.get(i),
                            Collectors.counting()),
                    Collectors.groupingBy(i -> dates.get(i),
                            Collectors.summingDouble(
                                    i -> temps.get(i))),
                    (counts, temp) -> temp.keySet().stream()
                            .collect(Collectors.toMap(k->k,
                                    k->Map.of("SUM", temp.get(k), "AVG",temp.get(k)
                                            / counts.get(k)), (m,n)->n, TreeMap::new))));



    for (String k : data.keySet())  {
        System.out.printf("For %s - sum of temperatures was %6.3f%n",k,data.get(k).get("SUM"));
        System.out.printf("For %s - average temp was %6.3f%n",k,data.get(k).get("AVG"));
        System.out.println();
     }

它打印以下内容:

For 1946-01-12 - sum of temperatures was -3.800
For 1946-01-12 - average temp was -1.267

For 1946-01-13 - sum of temperatures was -15.200
For 1946-01-13 - average temp was -5.067

For 1946-01-14 - sum of temperatures was -2.100
For 1946-01-14 - average temp was -0.700


【讨论】:

    【解决方案2】:

    这是您可以做到的一种方式。要像您一样获取 Weather 对象列表,我们使用 String 数组和 Double 值数组创建 Weather 对象列表。从该列表中,我们只需使用 Collectors#groupingBy 进行收集,它允许我们确定要分组的键,在本例中为 LocalDate,并且收集器用于将所有相关键值分组在一起,在本例中为 Collectors# summingDouble 因为我们想要对所有双精度值求和。

            String[] dates = {
                    "1946-01-12", "1946-01-12", "1946-01-12", "1946-01-13", "1946-01-13", "1946-01-13",
                    "1946-01-14", "1946-01-14", "1946-01-14"
            };
    
            double[] temperatures = {-1.3, 0.3, -2.8, -6.2, -4.7, -4.3, -1.5, -0.2, -0.4};
    
            class Weather {
    
                private final LocalDate date;
    
                private final double temperature;
    
                Weather(LocalDate date, double temperature) {
                    this.date = date;
                    this.temperature = temperature;
                }
    
                public LocalDate getDate() {
                    return date;
                }
    
                public double getTemperature() {
                    return temperature;
                }
            }
    
            List<Weather> weather = IntStream.range(0, dates.length)
                    .mapToObj(index -> new Weather(LocalDate.parse(dates[index]), temperatures[index]))
                    .collect(Collectors.toList());        
    
           Map<LocalDate, Double> temperaturesSum = weather.stream()
                    .collect(Collectors.groupingBy(Weather::getDate,
                            Collectors.summingDouble(Weather::getTemperature)));
    
            Map<LocalDate, Double> temperaturesAverage = weather.stream()
                    .collect(Collectors.groupingBy(Weather::getDate,
                            Collectors.averagingDouble(Weather::getTemperature)));
    
            System.out.println("sum: " + temperaturesSum);
            System.out.println("average: " + temperaturesAverage);
    

    输出

    sum: {1946-01-12=-3.8, 1946-01-13=-15.2, 1946-01-14=-2.1}
    average: {1946-01-12=-1.2666666666666666, 1946-01-13=-5.066666666666666, 1946-01-14=-0.7000000000000001}
    

    【讨论】:

    • 谢谢,我正在努力了解您的解决方案。我是否必须像您一样使用字符串数组创建天气对象列表?因为我的天气对象包含大约 20000 个日期和温度。
    • 不,您绝对不需要像我一样创建它。我不得不这样做,因为没有提供天气,也没有提供weatherData。
    【解决方案3】:

    希望这会有所帮助:

        List<String> dates = Arrays.asList("1946-01-12", "1946-01-12", "1946-01-12", "1946-01-13", "1946-01-13", "1946-01-13", "1946-01-14", "1946-01-14", "1946-01-14");
        List<Double> values = Arrays.asList(-1.3, 0.3, -2.8, -6.2, -4.7, -4.3, -1.5, -0.2, -0.4);
    
        Map<String, DoubleSummaryStatistics> res = IntStream
                .range(0, dates.size())
                .boxed()
                .map(index -> new Pair<>(dates.get(index), values.get(index)))
                .collect(groupingBy(Pair::getKey,
                        summarizingDouble(Pair::getValue)));
    
        System.out.println(res);
    

    结果:

    {1946-01-14=DoubleSummaryStatistics{count=3, sum=-2.100000, min=-1.500000, average=-0.700000, max=-0.200000}, 
    1946-01-12=DoubleSummaryStatistics{count=3, sum=-3.800000, min=-2.800000, average=-1.266667, max=0.300000}, 
    1946-01-13=DoubleSummaryStatistics{count=3, sum=-15.200000, min=-6.200000, average=-5.066667, max=-4.300000}}
    

    【讨论】:

    • 嘿,很遗憾,这并不能提供正确的结果。他们不想要列表中的最后一个条目,他们想要所有条目的总和。
    • 创建两个列表毫无意义......这完全容易出错和出错。已经有一个 Weather 对象保存了相应的日期和温度值。如果最终这两个列表的数量发生变化怎么办?现在我们不知道日期和值是否匹配。
    • 干得好,值得注意的一点是,您不需要先调用 entrySet 再调用流,再调用映射,再调用收集。在第一次收集调用中,只需使用 Collectors#groupingBy 并提供 Pair::getKey,然后不再再次映射,而是在 Pair::getValue 上调用 Collectors#summingInt。这将为您提供一个具有不同键的 Map 以及收集的所有值的总和。
    猜你喜欢
    • 1970-01-01
    • 2020-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    相关资源
    最近更新 更多