简体   繁体   English

Java 8 Collector.groupingBy下游的分类器值

[英]Java 8 Collector.groupingBy classifier value in downstream

What will be a proper way to provide classifier value into supplier function of the collector in the following example: 在以下示例中,将分类器值提供给收集器的供应商功能的正确方法是什么:

import static java.math.BigDecimal.*;
import static java.util.stream.Collector.*;
import static java.util.stream.Collectors.*;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class Test {

    public static class Item {
        String key;
        BigDecimal a;
        BigDecimal b;
        public Item(String key, BigDecimal a, BigDecimal b) {
            this.key = key;
            this.a = a;
            this.b = b;
        }
        public String getKey() {
            return key;
        }
        public BigDecimal getA() {
            return a;
        }
        public BigDecimal getB() {
            return b;
        }


    }
    public static class ItemSum {
        public ItemSum() {
        }

        public ItemSum(String key) {
            this.key = key;
        }
        String key;
        BigDecimal sumA = ZERO;
        BigDecimal sumB = ZERO;
        public void add(BigDecimal a, BigDecimal b) {
            sumA = sumA.add(a);
            sumB = sumB.add(b);
        }

        public ItemSum merge(ItemSum is) {
            sumA = sumA.add(is.getSumA());
            sumB = sumB.add(is.getSumB());
            return this;
        }
        public BigDecimal getSumA() {
            return sumA;
        }
        public BigDecimal getSumB() {
            return sumB;
        }


    }

    public static void main(String[] args) {

        Map<String, ItemSum> map = list().stream().collect(
                groupingBy(Item::getKey, 
                        of(
                                ItemSum::new, 
                                (s,i) -> {s.add(i.getA(), i.getB());},
                                (i,j) -> {return i.merge(j);}
                                )
                        )
                );
        map.forEach((k,v) -> {System.out.println(String.format("%s: A: %s: B: %s", k, v.getSumA(), v.getSumB()));});
    }

    public static List<Item> list() {
        List<Item> list = new ArrayList<>(3);
        list.add(new Item("a", ONE, ONE));
        list.add(new Item("a", ONE, ONE));
        list.add(new Item("b", ONE, ONE));
        return list;
    }

}

The problem is to pass key value into ItemSum during construction time. 问题是在构建期间将键值传递给ItemSum I've got some workarounds to populate key field afterwards but I'm wondering if there is a way to do it in supplier and eliminate default constructor of ItemSum . 之后我有一些解决方法来填充关键字段,但我想知道是否有办法在供应商中执行此操作并消除ItemSum的默认构造函数

This is a job for the toMap(keyMapper, valueMapper, mergeFunction) collector, not the groupingBy collector. 这是toMap(keyMapper, valueMapper, mergeFunction)收集器的作业,而不是groupingBy收集器。

Let's add a constructor 让我们添加一个构造函数

public ItemSum(Item item) {
    this.key = item.getKey();
    this.sumA = item.getA();
    this.sumB = item.getB();
}

to the class ItemSum . ItemSum类。 This constructor initializes one ItemSum based on an Item . 此构造函数基于Item初始化一个ItemSum You can then remove the default constructor (it won't be needed). 然后,您可以删除默认构造函数(不需要它)。 Then you can simply have the following: 然后你可以简单地拥有以下内容:

Map<String, ItemSum> map = 
    list().stream()
          .collect(Collectors.toMap(Item::getKey, ItemSum::new, ItemSum::merge));

What this does is that is collects each Item element into a map classified by the key of each Item . 这样做是将每个Item元素收集到按每个Item的键分类的地图中。 For the value, we initialize it with a single ItemSum . 对于该值,我们使用单个ItemSum对其进行初始化。 When multiple values are encountered for the same key, we merge them together. 当遇到同一个键的多个值时,我们将它们合并在一起。

In short, you can't do it the way you want. 简而言之,你不能按照你想要的方式去做。

If you make your ItemSum class receive an Item instance in your add method (the accumulator method of the collector), you could easily achieve what you want: 如果让ItemSum类在add方法(收集器的累加器方法)中接收Item实例,则可以轻松实现所需:

public class ItemSum {

    String key;

    BigDecimal sumA = ZERO;

    BigDecimal sumB = ZERO;

    public void add(Item item) {
        key = item.getKey();
        sumA = sumA.add(item.getA());
        sumB = sumB.add(item.getB());
    }

    public ItemSum merge(ItemSum is) {
        sumA = sumA.add(is.getSumA());
        sumB = sumB.add(is.getSumB());
        return this;
    }

    public BigDecimal getSumA() {
        return sumA;
    }

    public BigDecimal getSumB() {
        return sumB;
    }
}

Then, use it this way: 然后,以这种方式使用它:

Map<String, ItemSum> map = list().stream()
    .collect(Collectors.groupingBy(
        Item::getKey, 
        Collector.of(
            ItemSum::new, 
            ItemSum::add,
            ItemSum::merge)));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM