计算地图的值

Counting the values of a map

以下地图包含以下形式的数据:

1946-01-12;13:00:00;0.3;G
1946-01-12;18:00:00;-2.8;Y
1946-01-13;07:00:00;-6.2;G
1946-01-13;13:00:00;-4.7;G

日期是关键。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDate;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NavigableMap;
import java.util.TreeMap;

public class WeatherDataHandler {
    private NavigableMap<LocalDate, List<Weather>> weatherData =
            new TreeMap<>();
    
    public void loadData(String filePath) throws IOException {
        List<String> fileData =
                Files.readAllLines(Paths.get(filePath));
        for (String str : fileData) {
            List<String> parsed = parseData(str);
            LocalDate date = LocalDate.parse(parsed.get(0));
            LocalTime time = LocalTime.parse(parsed.get(1));
            double temperature = Double.parseDouble(parsed.get(2));
            String quality = parsed.get(3);
            Weather weather =
                    new Weather(date, time, temperature, quality);
            
            List<Weather> entries;
            entries = new ArrayList<Weather>();
            if (weatherData.get(date) == null) {
                entries.add(weather);
                weatherData.put(date, entries);
            } else {
                entries = weatherData.get(date);
                entries.add(weather);
            }
        }
    }
    
    private List<String> parseData(String str) {
        return Arrays.asList(str.split(";"));
    }
}

现在,我想实现一个方法来计算每个键的条目数,或者换句话说,每个日期在列表中出现的次数。它应 return 两个日期(即用户输入)之间的所有日期和每个键的值的数量。我从以下代码开始

/**
 * Search for missing values between the two dates (inclusive) assuming there
 * should be 24 measurement values for each day (once every hour). Result is
 * sorted by date.
 */
public Map<LocalDate, Integer> missingValues(LocalDate dateFrom, LocalDate dateTo) {

    Map<LocalDate, Integer> counts = weatherData.subMap(dateFrom , dateTo)
            .values().stream()
            .flatMap(List::stream)
            .filter(p -> p.getValue()
            .collect(Collectors.toMap(p -> p.getKey(), p -> p.getValue()));
    }
}

但我在使用过滤器和收集器方法时遇到了问题。我该如何完成?

使用 Collectors.groupingByWeather 的日期分组 class 和 Collectors.counting() 计算每组的大小。

Map<LocalDate, Long> counts = weatherData.subMap(dateFrom ,dateTo)
                                         .values()
                                         .stream()
                                         .flatMap(List::stream)
                        .collect(Collectors.groupingBy(p -> p.getDate(), Collectors.counting()));

您实际上需要的是将日期作为地图的关键元素进行分组。在值中,您想要每个键出现的次数。

为此,您必须在收集地图时使用 Collectors.groupingBy。对于价值,它只是 Collectors.counting()。请查看下面的代码片段以获取更多详细信息:

Map<LocalDate, Long> countMap = weatherData
                                .subMap(dateFrom ,dateTo)
                                .values()
                                .stream()
                                .flatMap(List::stream)
                                .collect(Collectors.groupingBy(p -> p.getDate(), Collectors.counting()));

由于您正在使用流来简化代码,因此您可能还想执行以下操作。

而不是做

    List<Weather> entries;
    entries = new ArrayList<Weather>();
    if (weatherData.get(date) == null) {
         entries.add(weather);
         weatherData.put(date, entries);
    } else {
        entries = weatherData.get(date);
        entries.add(weather);
    }

你可以做到

     weatherData.computeIfAbsent(date, k->new ArrayList<>()).add(weather);

它说如果键 date 不存在,则输入一个新的 ArrayList 作为值。无论如何,return value。由于值是刚刚输入或已经存在的 list,因此您可以简单地将 weather 实例添加到列表中。

编译器已经知道列表的类型,因为它是从您的 NavigableMap 实例派生的。

如果您想先检查一下,请尝试这个简单的示例。

Map<Integer,List<Integer>> map = new HashMap<>();
System.out.println(map);
map.computeIfAbsent(10, k-> new ArrayList<>()).add(20);
System.out.println(map);
map.computeIfAbsent(10, k-> new ArrayList<>()).add(30);
System.out.println(map);

computeIfAbsent 自 Java 1.8

以来可用