2015-11-09 34 views
4

我有上每一行包含对名称和数量的这样的文件,文本文件:总和在地图值的特定键

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(); 
    } 
    ; 

} 

如何实际上当你想要写功能,我可以提高更多的功能性的方式这种代码,abillity更多(使用并行流等)

回答

9

同时处理数据的解决了这个代码,规则是:不要使用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])))); 

在这段代码中,我们每个分割线。

  • classifier,它是分类的每个项目来提取所得Map的键的功能,只返回该行的第一部分
  • downstream是:然后,我们使用Collectors.groupingBy(classifier, downstream)其中分组的流收集器减少每个具有相同键的值:在这种情况下,它是Collectors.summingInt(mapper),它将由给定的映射器提取的每个整数相加。

作为一个侧面说明(而只是让你知道),你可以重写你的整个forEach更简单地使用新Map.merge(key, value, remappingFunction)方法,只需一个电话:

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

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

+0

很好的解释!那么在示例中的并发处理(并行流使用)呢? –

+4

@GerardRozsavolgyi我不确定读取文件中的行是否会并行化,但这只是在Stream流水线上调用“parallel()”的问题。也许最好是收集列表中的所有行,并调用该列表上的'parallelStream()',以便按行的第一部分对其进行分组。 – Tunaki