【问题标题】:Java 8 streams: sum values on a distinct keyJava 8 流:对不同键的值求和
【发布时间】:2018-03-03 13:25:17
【问题描述】:

我有一个包含以下列标题的行的文件:

CITY_NAME  COUNTY_NAME  POPULATION

Atascocita  Harris  65844
Austin  Travis  931820
Baytown Harris  76335
...

我正在使用流来尝试生成类似于以下内容的输出:

COUNTY_NAME  CITIES_IN_COUNTY  POPULATION_OF_COUNTY
Harris  2  142179
Travis  1  931820
...

到目前为止,我已经能够使用流来获取不同县名的列表(因为这些名称是重复的),但现在我在获取不同县的城市数量以及人口总和时遇到了问题这些县的城市。我已将文件读入 texasCitiesClass 类型的 ArrayList,到目前为止,我的代码如下所示:

public static void main(String[] args) throws FileNotFoundException, IOException {
    PrintStream output = new PrintStream(new File("output.txt"));
    ArrayList<texasCitiesClass> txcArray = new ArrayList<texasCitiesClass>();
    initTheArray(txcArray); // this method will read the input file and populate an arraylist
    System.setOut(output);

    List<String> counties;
    counties = txcArray.stream()
            .filter(distinctByKey(txc -> txc.getCounty())) // grab distinct county names
            .distinct() // redundant?
            .sorted((txc1, txc2) -> txc1.getCounty().compareTo(txc2.getCounty())); // sort alphabetically

}

public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
    Map<Object, String> seen = new ConcurrentHashMap<>();
    return t -> seen.put(keyExtractor.apply(t), "") == null;
}    

此时,我有一个包含唯一县名的流。由于 sorted() 运算符将返回一个新流,我如何获得(并因此求和)县的人口值?

【问题讨论】:

  • 这段代码还能编译吗? counties 是一个 List?
  • 你的意思是Map&lt;String,Long&gt; counties = txcArray.stream() .collect(Collectors.groupingBy(txc -&gt; txc.getCounty(), Collectors.counting()));

标签: java lambda stream java-stream


【解决方案1】:

给定类(ctor、getter、setter 省略)

class Foo {
    String name;
    String countyName;
    int pop;
}

class Aggregate {
      String name;
      int count;
      int pop;
}

您可以通过使用Collectors.toMap 将它们映射到聚合对象并使用它的mergeFunction 合并它们来聚合您的值。使用 TreeMap,其条目按其键排序。

TreeMap<String, Aggregate> collect = foos.stream()
        .collect(Collectors.toMap(
                Foo::getCountyName,
                foo -> new Aggregate(foo.countyName,1,foo.pop),
                (a, b) -> new Aggregate(b.name, a.count + 1, a.pop + b.pop),
                TreeMap::new)
        );

使用

List<Foo> foos = List.of(
        new Foo("A", "Harris", 44),
        new Foo("C", "Travis  ", 99),
        new Foo("B", "Harris", 66)
);

地图是

{Harris=Aggregate{name='Harris', count=2, pop=110}, Travis =Aggregate{name='Travis', count=1, pop=99}}

【讨论】:

  • 您可以简单地使用Map&lt;String,IntSummaryStatistics&gt; counties = foos.stream() .collect(Collectors.groupingBy(foo -&gt; foo.countyName, TreeMap::new, Collectors.summarizingInt(foo -&gt; foo.pop))); 一次获取所有信息,而无需额外的Aggregate 类,因为IntSummaryStatistics 包含count 和sum。
  • @Holger: 不错,但是如果要累积多个值...
猜你喜欢
  • 2019-10-01
  • 2023-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-27
  • 2019-02-17
  • 2015-07-13
  • 1970-01-01
相关资源
最近更新 更多