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

时间:2018-12-20 15:03:39

标签: java java-8 java-stream grouping

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

我上课

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

   // constructor
}

,我需要将其转换为

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

       // constructor
    }

因此,意图是按用户名分组,不计算taskId,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)));

4 个答案:

答案 0 :(得分:3)

可能可以通过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());

答案 1 :(得分:1)

您可以按照以下步骤进行操作:

按名称区分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<ToQ>

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

假定这些类具有 getters setters 以及适当的 constructor

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

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

答案 2 :(得分:1)

简单的方法是这样的:将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;
}

答案 3 :(得分:0)

您也可以使用一个衬纸来尝试:

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