地图中特定键的值总和

Sum of values in the map for specific key

我有一个文本文件,其中的每一行都包含一对名称和数量,如下所示:

Mike 5
Kate 2
Mike 3

我需要按键对这些值求和。我是这样解决的

public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<String, Integer>();
    try {
        Files.lines(Paths.get("/Users/walter/Desktop/stuff.txt"))
                .map(line -> line.split("\s+")).forEach(line -> {
                    String key = line[0];
                    if (map.containsKey(key)) {
                        Integer oldValue = map.get(key);
                        map.put(key, oldValue + Integer.parseInt(line[1]));
                    } else {
                        map.put(line[0], Integer.parseInt(line[1]));
                    }
                });
        map.forEach((k,v) -> System.out.println(k + " " +v));

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    ;

}

实际上我如何以更实用的方式改进此代码,并能够更并发地处理数据(使用并行流等)

当你想写函数式代码时,规则是:不要使用forEach。这是一个命令式解决方案,会破坏功能代码。

你想要的是将每一行拆分并按第一部分(键)分组,同时对第二部分(值)求和:

Map<String, Integer> map = 
    Files.lines(Paths.get("/Users/walter/Desktop/stuff.txt"))
         .map(s -> s.split("\s+"))
         .collect(groupingBy(a -> a[0], summingInt(a -> Integer.parseInt(a[1]))));

在这段代码中,我们拆分了每一行。然后,我们使用 Collectors.groupingBy(classifier, downstream) 对 Stream 进行分组,其中:

  • classifier,这是一个函数,对每一项进行分类,提取结果Map的key,只是returns行的第一部分
  • downstream 是一个收集器,它减少每个具有相同键的值:在这种情况下,它是 Collectors.summingInt(mapper) 对给定映射器提取的每个整数求和。

作为旁注(如您所知),您可以使用新的 Map.merge(key, value, remappingFunction) 方法更简单地重写整个 forEach,只需调用:

map.merge(line[0], Integer.valueOf(line[1]), Integer::sum);

如果此键不存在,这将使用键 line[0] 和值 Integer.valueOf(line[1]) 放置一个新值,否则,它将使用给定的重新映射函数更新键(这是本例中新旧值之和)。