【问题标题】:Is there an aggregateBy method in the stream Java 8 api?流Java 8 api中是否有aggregateBy方法?
【发布时间】:2014-07-13 22:17:46
【问题描述】:

遇到这个非常有趣但一岁的presentation by Brian Goetz - 在链接的幻灯片中,他提出了一个据称在 Stream API 中的 aggregateBy() 方法,该方法应该将列表的元素 (?) 聚合到一张地图(给定一个默认初始值和一个操作该值的方法(也适用于重复的键) - 请参阅演示文稿中的下一张幻灯片)。

Stream API 中显然没有这种方法。在 Java 8 中是否有另一种方法可以做类似的事情?

【问题讨论】:

  • +1,很好的问题。
  • 顺便说一句,如果你喜欢这种东西,Brian Goetz 也做了this presentation;非常非常有启发性。

标签: java java-8 aggregation


【解决方案1】:

假设我们有一个员工列表,其中包含他们的部门和薪水,我们想要每个部门支付的总薪水。

有几种方法可以做到这一点,例如,您可以使用toMap 收集器来汇总每个部门的数据:

  • 第一个参数是键映射器(您的聚合轴 = 部门),
  • 第二个是值映射器(您要聚合的数据 = 薪水),并且
  • 第三个是合并功能(您希望如何聚合数据 = 对值求和)。

例子:

import static java.util.stream.Collectors.*;

public static void main(String[] args) {
  List<Person> persons = Arrays.asList(new Person("John", "Sales", 10000),
                                       new Person("Helena", "Sales", 10000),
                                       new Person("Somebody", "Marketing", 15000));

  Map<String, Double> salaryByDepartment = persons.stream()
          .collect(toMap(Person::department, Person::salary, (s1, s2) -> s1 + s2));
  System.out.println("salary by department = " + salaryByDepartment);
}

与流一样,有几种方法可以获得所需的结果,例如:

import static java.util.stream.Collectors.*;

Map<String, Double> salaryByDepartment = persons.stream()
        .collect(groupingBy(Person::department, summingDouble(Person::salary)));

Person 类供参考:

static class Person {
  private final String name, department;
  private final double salary;
  public Person(String name, String department, double salary) {
    this.name = name;
    this.department = department;
    this.salary = salary;
  }
  public String name() { return name; }
  public String department() { return department; }
  public double salary() { return salary; }
}

【讨论】:

    【解决方案2】:

    可以使用Collectors 类来完成聚合操作。所以在视频中,这个例子相当于:

    Map<String, Integer> map = 
        documents.stream().collect(Collectors.groupingBy(Document::getAuthor, Collectors.summingInt(Document::getPageCount)));
    

    groupingBy 方法将为您提供Map&lt;String, List&lt;Document&gt;&gt;。现在您必须使用下游收集器来汇总与每个键关联的List 中每个文档的所有页数。

    这是通过为groupingBy 提供下游收集器来完成的,即summingInt,从而产生Map&lt;String, Integer&gt;


    他们在文档中给出了基本相同的示例,他们按部门计算员工工资的总和。

    我认为他们删除了此操作并创建了 Collectors 类,而不是拥有一个有用的类,其中包含您将常用的大量缩减。

    【讨论】:

    • 事实上,Collectors 不仅仅有助于聚合;你还有Collectors.toList()等基本的东西;从长远来看,所有这些操作都在同一个“功能包”中会变得相当混乱。我猜 JDK 可以使用 Aggregates 类...
    • @fge 也许 Brian Goetz 会看到这个帖子并解释其中的原因,那会很有趣。
    • 是的,aggregateBy() 是对现在在 Collector 中体现的概念的早期尝试。 (虽然我认为引用的示例是一年多以前的事了。) Collector 解决的 aggregateBy() 的一个问题是它只“深入一层”。 Collectors.groupingBy() 使用“下游”收集器解决了这个问题,并且更加可组合。
    • @BrianGoetz 好的,现在我看到了Collector 界面,所有这些都在Collectors 中是有道理的,但是我需要时间来掌握这个界面背后的概念!我猜Collector接口的解释才是这个问题的真正答案
    • @fge 这将是一个更长的解释!但坚持下去,值得。一旦你掌握了概念,它就会非常强大。
    【解决方案3】:

    This particular Javadoc entry 是我在 Java 8 中可以找到的关于这个聚合的最接近的东西。即使它是第三方 API,签名似乎排列得很好——你提供了一些函数来从中获取值,一些值的终端函数(在这种情况下为零),以及将函数和值组合在一起的某个函数。

    感觉很像Collector,它将为我们提供执行此操作的能力。

    Map<String, Integer> strIntMap =
        intList.stream()
               .collect(Collectors
                   .groupingBy(Document::getAuthor,
                    Collectors.summingInt(Document::getPageCount)));
    

    然后的想法是,我们将列表中每个条目的作者姓名分组,并将作者拥有的总页码添加到 Map&lt;String, Integer&gt;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-27
      • 1970-01-01
      • 1970-01-01
      • 2014-04-26
      • 1970-01-01
      相关资源
      最近更新 更多