【问题标题】:Mapping one object to another with aggregation and grouping使用聚合和分组将一个对象映射到另一个对象
【发布时间】:2018-12-20 15:03:39
【问题描述】:

通过聚合和分组将一个对象映射到另一个对象

我有课

class Q {
    String username;
    int taskId;
    double timediff;

   // constructor
}

我需要把它转换成

 class ToQ {
        String username;
        int noOfTasks;
        double sum;

       // constructor
    }

所以内涵是按用户名分组,taskIds的计数,timediff的总和。

这个列表

List<Q> qs = new ArrayList<>();
    qs.add(new Q("tom", 2, 2.0));
    qs.add(new Q("tom", 6, 1.0));
    qs.add(new Q("harry", 8, 0.03));

ToQ 的输出应该为

new ToQ("tom",2,3);
new ToQ("harry",1,0.03);

我尝试使用分组功能,但它生成 HashMap 但不确定如何转换为对象。

Map<String, Double> collect = qs
            .stream()
            .collect(groupingBy(Q::getUsername, summingDouble(Q::getLocalDateTime)));

【问题讨论】:

  • 我想知道“stark”和“tom”之间的关系是什么?
  • 它们是一样的 :-)

标签: java java-8 java-stream grouping


【解决方案1】:

可能通过toMap收集器更好地完成:

Collection<ToQ> values = qs.stream().collect(toMap(Q::getUsername,
            v -> new ToQ(v.getUsername(), 1, v.getTimediff()),
            (l, r) -> {
                l.setNoOfTasks(l.getNoOfTasks() + r.getNoOfTasks());
                l.setSum(l.getSum() + r.getSum());
                return l;
            })).values();

这假设您有一个Q 构造函数,如下所示:

public ToQ(String username, int noOfTasks, double sum) {...}

如果你想坚持groupingBy,那么你可以这样做:

List<ToQ> result = qs.stream()
                .collect(groupingBy(Q::getUsername))
                .values()
                .stream()
                .map(l -> new ToQ(l.get(0).getUsername(), l.size(), l.stream().mapToDouble(Q::getTimediff).sum()))
                .collect(toList());

【讨论】:

  • 我实际上希望最终输出为 Collection。我会通过你的答案。
【解决方案2】:

你可以这样做:

TimeDiff 按名称分组

Map<String, Double> nameToTimeDiffSumMap = qs.stream()
        .collect(Collectors.groupingBy(Q::getUsername, 
                Collectors.summingDouble(Q::getTimediff)));

按名称分组计数

Map<String, Long> nameToCountMap = qs.stream()
        .collect(Collectors.groupingBy(Q::getUsername, 
                Collectors.counting()));

形成List&lt;ToQ&gt;

List<ToQ> toQS = qs.stream().map(a -> 
        new ToQ(a.getUsername(), nameToCountMap.get(a.getUsername()), nameToTimeDiffSumMap.get(a.getUsername())))
        .collect(toList());

假定类具有 getterssetters 以及适当的 constructor

class ToQ {
    String username;
    long noOfTasks;
    double sum;
}

class Q {
    String username;
    int taskId;
    double timediff;
}

【讨论】:

    【解决方案3】:

    简单的方法是这样的:使用 toMap() 和合并功能。

     Map<String, ToQ> map = qs.stream()
          .collect(toMap(Q::getUsername, q -> new ToQ(q.username, q.taskId, q.timediff), ToQ::merge));
     List<ToQ> toQList =  new ArrayList<>( map.values());
    

    并在 ToQ 中定义一个方法:

    public ToQ merge(ToQ q){
         this.noOfTasks+=q.noOfTasks;
         this.sum+=q.sum;
         return this;
    }
    

    【讨论】:

      【解决方案4】:

      你也可以用一个衬里试试:

      List<ToQ> toQS = qs.stream()
          .map(Q::getUserName)
          .distinct()
          .map(userName -> new ToQ(
              userName,
              qs.stream()
                  .map(Q::getUserName)
                  .filter(userName::equals)
                  .count(),
              qs.stream()
                  .map(Q::getUserName)
                  .filter(q -> Objects.equals(userName, q.getUserName()))
                  .mapToDouble(Q::getTimeDiff)
                  .sum()
          ))
          .collect(toList())
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-10
        • 2017-01-26
        • 1970-01-01
        • 2021-04-28
        • 1970-01-01
        • 2019-04-27
        相关资源
        最近更新 更多