微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

分组和求和对象如带有Java Lambda的SQL中的对象?

如何解决分组和求和对象如带有Java Lambda的SQL中的对象?

使用Collectors.groupingBy是正确的方法,但不要使用单个参数版本来创建每个组的所有项目列表,而应使用两个arg版本,后者使用另一个参数版本Collector来确定如何汇总每个组的元素。

当您要汇总元素的单个属性或仅计算每个组中元素的数量时,这特别平滑:

数数:

list.stream()
  .collect(Collectors.groupingBy(foo -> foo.id, Collectors.counting()))
  .forEach((id,count)->System.out.println(id+"\t"+count));

总结一个属性

list.stream()
  .collect(Collectors.groupingBy(foo -> foo.id,
                                    Collectors.summingInt(foo->foo.targetCost)))
  .forEach((id,sumTargetCost)->System.out.println(id+"\t"+sumTargetCost));

在您要聚合多个属性的情况下,指定一种自定义归约操作(如此答案中所建议的那样)是正确的方法,但是,您可以在分组操作期间执行归约操作,因此无需将整个数据收集到Map<…,List>执行还原之前的a :

(我假设您import static java.util.stream.Collectors.*;现在使用…)

list.stream().collect(groupingBy(foo -> foo.id, collectingAndThen(reducing(
  (a,b)-> new Foo(a.id, a.ref, a.targetCost+b.targetCost, a.actualCost+b.actualCost)),
      Optional::get)))

.forEach((id,foo)->System.out.println(foo)); 为了完整起见,这里是一个超出您问题范围的问题的解决方案:如果要GROUP BY多个列/属性该怎么办?

跳入程序员头脑的第一件事是用来groupingBy提取流元素的属性并创建/返回新的关键对象。但这要求键属性具有适当的holder类(而Java没有通用的Tuple类)。

但是还有另一种选择。通过使用三参数形式,groupingBy我们可以为实际Map实现指定供应商,该供应商将确定键的相等性。通过使用带有比较器比较多个属性的排序映射,我们无需其他类即可获得所需的行为。我们只需要注意不要使用比较器忽略的关键实例中的属性,因为它们只有任意值:

list.stream().collect(groupingBy(Function.identity(),
  ()->new TreeMap<>(
    // we are effectively grouping by [id, actualCost]
    Comparator.<Foo,Integer>comparing(foo->foo.id).thenComparing(foo->foo.actualCost)
  ), // and aggregating/ summing targetCost
  Collectors.summingInt(foo->foo.targetCost)))
.forEach((group,targetCostSum) ->
    // take the id and actualCost from the group and actualCost from aggregation
    System.out.println(group.id+"\t"+group.actualCost+"\t"+targetCostSum));

这是一种可能的方法

public class Test {
    private static class Foo {
        public int id, targetCost, actualCost;
        public String ref;

        public Foo(int id, String ref, int targetCost, int actualCost) {
            this.id = id;
            this.targetCost = targetCost;
            this.actualCost = actualCost;
            this.ref = ref;
        }

        @Override
        public String toString() {
            return String.format("Foo(%d,%s,%d,%d)",id,ref,targetCost,actualCost);
        }
    }

    public static void main(String[] args) {
        List<Foo> list = Arrays.asList(
            new Foo(1, "P1", 300, 400), 
            new Foo(2, "P2", 600, 400),
            new Foo(3, "P3", 30, 20),
            new Foo(3, "P3", 70, 20),
            new Foo(1, "P1", 360, 40),
            new Foo(4, "P4", 320, 200),
            new Foo(4, "P4", 500, 900));

        List<Foo> transform = list.stream()
            .collect(Collectors.groupingBy(foo -> foo.id))
            .entrySet().stream()
            .map(e -> e.getValue().stream()
                .reduce((f1,f2) -> new Foo(f1.id,f1.ref,f1.targetCost + f2.targetCost,f1.actualCost + f2.actualCost)))
                .map(f -> f.get())
                .collect(Collectors.toList());
        System.out.println(transform);
    }
}

输出

[Foo(1,P1,660,440), Foo(2,P2,600,400), Foo(3,P3,100,40), Foo(4,P4,820,1100)]

解决方法

我有一个Foo与这些领域的课程:

id:int /名称;字符串/ targetCost:BigDecimal / actualCost:BigDecimal

我得到了此类对象的数组列表。例如:

new Foo(1,"P1",300,400),new Foo(2,"P2",600,new Foo(3,"P3",30,20),70,new Foo(1,360,40),new Foo(4,"P4",320,200),500,900)

我想通过创建“ targetCost”和“ actualCost”的总和并将“行”分组来转换这些值

new Foo(1,660,440),100,820,1100)

我现在写的是:

data.stream()
       .???
       .collect(Collectors.groupingBy(PlannedProjectPOJO::getId));

我怎样才能做到这一点?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。