Java 8流按3个字段分组,按sum和count聚合产生单行输出 [英] Java 8 streams group by 3 fields and aggregate by sum and count produce single line output

查看:923
本文介绍了Java 8流按3个字段分组,按sum和count聚合产生单行输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道在论坛中提出了类似的问题,但他们似乎都没有完全解决我的问题。现在我对Java 8很新,所以请耐心等待。
我有一个产品清单,例如:

I know there a similar questions asked in the forum but none of them seem to be addressing my problem fully. Now I'm very new to Java 8, so please bear with me. I have a list of Products, for example:

Input:
name    category    type    cost
prod1       cat2     t1      100.23
prod2       cat1     t2      50.23
prod1       cat1     t3      200.23
prod3       cat2     t1      150.23
prod1       cat2     t1      100.23


Output:
Single line (name, category, type) summing the cost and count of products.




Product {
    public String name;
    public String category;
    public String type;
    public int id;
    public double cost;

}

我需要按名称,类别和类型对此进行分组产生一个结果,
汇总了这些数据,并产生了每种产品的总成本和数量。大多数示例显示按两个字段进行分组并使用单个条件进行聚合。

I need to group this by name, category and type and produce a single result that summarizes this data and produces the total cost and count of each product. Most examples show grouping by two fields and aggregating using single criteria.

根据forumn的建议,我想出了这个分组:

Following suggestions on the forumn, i came up with this for groupings:

    public class ObjectKeys {

    ArrayList<Object> keys;

    public ObjectKeys(Object...searchKeys) {

         keys = new ArrayList<Object>();

            for (int i = 0; i < searchKeys.length; i++) {
                keys.add( searchKeys[i] );
            }
    }

}

然后使用如下:

Map<String, Map<String, Map<String, List<Product>>>> productsByNameCategoryType =
    products.stream().collect(groupingBy(new ObjectKeys(l.name(), l.category(),l.type())))

但是如何将count和sum连接到上面的代码?特别是对于超过2个领域的团体。
有更好的方法吗?

But how do I chain count and sum to the the above code? Especially for group by with more than 2 fields. Is there a better way to do this?

就像我提到的,我的Java8不太好,请帮忙。

Like I mentioned my Java8 is not that good, please help.

推荐答案

前提条件



Precondition

class Product {
    public String name;
    public String category;
    public String type;
    public int id; 
    //todo:implement equals(), toString() and hashCode()
 }

class Item{
   public Product product;
   public double cost;
}



汇总方式



您可以使用收藏家#groupingBy & 收藏家#summarizingDouble

List<Item> items = ...; 
Map<Product, DoubleSummaryStatistics> stat = items.stream().collect(groupingBy(
            it -> it.product,
            Collectors.summarizingDouble(it -> it.cost)
));

// get some product summarizing
long count = stat.get(product).getCount();
double sum = stat.get(product).getSum();

//list all product summarizing
stat.entrySet().forEach(it ->
  System.out.println(String.format("%s - count: %d, total cost: %.2f"
        , it.getKey(),it.getValue().getCount(), it.getValue().getSum()));
);



合并具有相同产品的商品



首先,您需要在 Item 类中添加 qty 字段:

class Item{
   public int qty;
   //other fields will be omitted

   public Item add(Item that) {
        if (!Objects.equals(this.product, that.product)) {
            throw new IllegalArgumentException("Can't be added items"
                     +" with diff products!");
        }
        return from(product, this.cost + that.cost, this.qty + that.qty);
    }

    private static Item from(Product product, double cost, int qty) {
        Item it = new Item();
        it.product = product;
        it.cost = cost;
        it.qty = qty;
        return it;
    }

}

那么你可以使用 Collectors#toMap 合并具有相同产品的商品:

then you can using Collectors#toMap to merges items with same product:

Collection<Item> summarized = items.stream().collect(Collectors.toMap(
        it -> it.product,
        Function.identity(),
        Item::add
)).values();



最后



你可以看到两种方式做同样的事情,但第二种方法更容易在流上运行。以及我在github中检查过的两种方法的测试,你可以点击查看更多细节:汇总项目& 合并项目

这篇关于Java 8流按3个字段分组,按sum和count聚合产生单行输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆