Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/324.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用groupBy创建其值为BigDecimal字段平均值的映射?_Java_Java 8_Java Stream_Bigdecimal - Fatal编程技术网

Java 如何使用groupBy创建其值为BigDecimal字段平均值的映射?

Java 如何使用groupBy创建其值为BigDecimal字段平均值的映射?,java,java-8,java-stream,bigdecimal,Java,Java 8,Java Stream,Bigdecimal,我有以下课程: final public class Person { private final String name; private final String state; private final BigDecimal salary; public Person(String name, String state, BigDecimal salary) { this.name = name; this.state = state; this.sal

我有以下课程:

final public class Person {

 private final String name;
 private final String state;
 private final BigDecimal salary;

 public Person(String name, String state, BigDecimal salary) {
    this.name = name;
    this.state = state;
    this.salary = salary;
 }

 //getters omitted for brevity...
}
我想创建一个地图,列出各州的平均工资。如何使用Java8流来实现这一点?我试图在groupBy上使用下游收集器,但未能以优雅的方式做到这一点

我做了以下工作,但看起来很可怕:

Stream.of(p1,p2,p3,p4).collect(groupingBy(Person::getState, mapping(d -> d.getSalary(), toList())))
.forEach((state,wageList) -> {
        System.out.print(state+"-> ");
        final BigDecimal[] wagesArray = wageList.stream()
                .map(bd -> new BigDecimal[]{bd, BigDecimal.ONE})
                .reduce((a, b) -> new BigDecimal[]{a[0].add(b[0]), a[1].add(BigDecimal.ONE)})
                .get();
        System.out.println(wagesArray[0].divide(wagesArray[1])
                                        .setScale(2, RoundingMode.CEILING));
    });

有更好的方法吗?

这里有一种方法。尽管我仍然相信还有其他更好的方法

Map<String, Double> collect = Arrays.asList(p, p1, p2, p3, p4)
            .stream()
            .collect(groupingBy(k -> k.getState(), mapping(v -> v.salary, toList())))
            .entrySet()
            .stream()
            .collect(toMap(k -> k.getKey(), v -> v.getValue().stream().mapToDouble(BigDecimal::doubleValue).average().getAsDouble()));
Map collect=Arrays.asList(p、p1、p2、p3、p4)
.stream()
.collect(groupingBy(k->k.getState(),映射(v->v.salary,toList()))
.entrySet()
.stream()
.collect(toMap(k->k.getKey(),v->v.getValue().stream().mapToDouble(BigDecimal::doubleValue).average().getAsDouble());

下面是一个仅使用BigDecimal算法的完整示例,并展示了如何实现自定义收集器

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public final class Person {

    private final String name;
    private final String state;
    private final BigDecimal salary;

    public Person(String name, String state, BigDecimal salary) {
        this.name = name;
        this.state = state;
        this.salary = salary;
    }

    public String getName() {
        return name;
    }

    public String getState() {
        return state;
    }

    public BigDecimal getSalary() {
        return salary;
    }

    public static void main(String[] args) {
        Person p1 = new Person("John", "NY", new BigDecimal("2000"));
        Person p2 = new Person("Jack", "NY", new BigDecimal("3000"));
        Person p3 = new Person("Jane", "GA", new BigDecimal("1500"));
        Person p4 = new Person("Jackie", "GA", new BigDecimal("2500"));

        Map<String, BigDecimal> result =
            Stream.of(p1, p2, p3, p4).collect(
                Collectors.groupingBy(Person::getState,
                                      Collectors.mapping(Person::getSalary,
                                                         new AveragingCollector())));
        System.out.println("result = " + result);

    }

    private static class AveragingCollector implements Collector<BigDecimal, IntermediateResult, BigDecimal> {
        @Override
        public Supplier<IntermediateResult> supplier() {
            return IntermediateResult::new;
        }

        @Override
        public BiConsumer<IntermediateResult, BigDecimal> accumulator() {
            return IntermediateResult::add;
        }

        @Override
        public BinaryOperator<IntermediateResult> combiner() {
            return IntermediateResult::combine;
        }

        @Override
        public Function<IntermediateResult, BigDecimal> finisher() {
            return IntermediateResult::finish
        }

        @Override
        public Set<Characteristics> characteristics() {
            return Collections.emptySet();
        }
    }

    private static class IntermediateResult {
        private int count = 0;
        private BigDecimal sum = BigDecimal.ZERO;

        IntermediateResult() {
        }

        void add(BigDecimal value) {
            this.sum = this.sum.add(value);
            this.count++;
        }

        IntermediateResult combine(IntermediateResult r) {
            this.sum = this.sum.add(r.sum);
            this.count += r.count;
            return this;
        }

        BigDecimal finish() {
            return sum.divide(BigDecimal.valueOf(count), 2, BigDecimal.ROUND_HALF_UP);
        }
    }
}
import java.math.BigDecimal;
导入java.util.Collections;
导入java.util.Map;
导入java.util.Set;
导入java.util.function.BiConsumer;
导入java.util.function.BinaryOperator;
导入java.util.function.function;
导入java.util.function.Supplier;
导入java.util.stream.Collector;
导入java.util.stream.collector;
导入java.util.stream.stream;
公开期末班学员{
私有最终字符串名;
私有最终字符串状态;
私人薪酬;
公共人物(字符串名称、字符串状态、BigDecimal工资){
this.name=名称;
this.state=状态;
这个。薪水=薪水;
}
公共字符串getName(){
返回名称;
}
公共字符串getState(){
返回状态;
}
公共工资(){
返回工资;
}
公共静态void main(字符串[]args){
人员p1=新人员(“约翰”、“纽约”、新大十进制(“2000”);
人员p2=新人员(“杰克”、“纽约”、新大十进制(“3000”);
人员p3=新人员(“简”、“GA”、新的大十进制(“1500”);
人员p4=新人员(“Jackie”,“GA”,新的大十进制(“2500”);
映射结果=
流(p1、p2、p3、p4)。收集(
Collectors.groupingBy(Person::getState,
Collectors.mapping(Person::getSalary,
新的AveragingCollector());
System.out.println(“结果=”+结果);
}
私有静态类AveragingCollector实现收集器{
@凌驾
公共供应商(){
返回中间结果::新建;
}
@凌驾
公共双消费者累加器(){
返回中间结果::添加;
}
@凌驾
公共二进制运算符组合器(){
返回中间结果::合并;
}
@凌驾
公共函数完成器(){
返回中间结果::finish
}
@凌驾
公共集特征(){
返回集合;
}
}
私有静态类中间结果{
私有整数计数=0;
私有BigDecimal和=BigDecimal.0;
中间结果(){
}
无效添加(BigDecimal值){
this.sum=this.sum.add(值);
这个.count++;
}
中间结果联合收割机(中间结果r){
this.sum=this.sum.add(r.sum);
this.count+=r.count;
归还这个;
}
BigDecimal finish(){
返回sum.divide(BigDecimal.valueOf(count),2,BigDecimal.ROUND\u HALF\u UP);
}
}
}
如果您接受将BigDecimal值转换为double(这对于平均工资来说是完全可以接受的,IMHO),您可以使用

Map<String, Double> result2 =
            Stream.of(p1, p2, p3, p4).collect(
                Collectors.groupingBy(Person::getState,
                                      Collectors.mapping(Person::getSalary,
                                                         Collectors.averagingDouble(BigDecimal::doubleValue))));
映射结果2=
流(p1、p2、p3、p4)。收集(
Collectors.groupingBy(Person::getState,
Collectors.mapping(Person::getSalary,
收集器.averagingDouble(BigDecimal::doubleValue));

注意:这不是最好的解决方案。请阅读下面的(从上一页的编辑部分)了解更好的方法

您可以使用将每个州的工资累积到
ArrayList
,然后使用finisher函数计算每个州的平均值:

Map<String, BigDecimal> salariesByState = Stream.of(p1, p2, p3, p4).collect(
    Collectors.groupingBy(Person::getState,
        Collectors.mapping(Person::getSalary,
            Collector.<BigDecimal, List<BigDecimal>, BigDecimal>of(
                ArrayList::new, // create accumulator
                List::add,      // add to accumulator
                (l1, l2) -> {   // combine two partial accumulators
                    l1.addAll(l2);
                    return l1;
                },
                l -> l.stream() // finish with a reduction that returns average
                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                    .divide(BigDecimal.valueOf(l.size()))))));
Acc
是用于累积部分结果(部分和和和计数)的类

现在,我们可以将
Collector.of
用于此类:

Map<String, BigDecimal> salariesByState = Stream.of(p1, p2, p3, p4).collect(
    Collectors.groupingBy(Person::getState,
        Collectors.mapping(Person::getSalary,
            Collector.of(Acc::new, Acc::add, Acc::merge, Acc::avg))));
现在可以按如下方式使用此方法:

Map<String, BigDecimal> salariesByState = Stream.of(p1, p2, p3, p4).collect(
    Collectors.groupingBy(Person::getState,
        Collectors.mapping(Person::getSalary, averagingBigDecimal())));
Map salariesByState=Stream.of(p1、p2、p3、p4)。收集(
Collectors.groupingBy(Person::getState,
映射(Person::getSalary,averagingBigDecimal());

如果您需要
BigDecimal
精度,并且您不介意额外的迭代,您可以这样做:

static Map<String, BigDecimal> averageByState(List<Person> persons) {
    // collect sums
    Map<String, BigDecimal> sumByState = persons.stream()
            .collect(groupingBy(
                    Person::getState,
                    HashMap::new,
                    mapping(Person::getSalary, reducing(BigDecimal.ZERO, BigDecimal::add))));

    // collect counts
    Map<String, Long> countByState = persons.stream()
            .collect(groupingBy(Person::getState, counting()));

    // merge
    sumByState.replaceAll((state, sum) -> sum.divide(BigDecimal.valueOf(countByState.get(state))));
    return sumByState;
}
静态地图按状态平均(列出人员){
//集资
Map sumByState=persons.stream()
.收集(分组)(
Person::getState,
HashMap::新建,
映射(Person::getSalary,reduce(BigDecimal.ZERO,BigDecimal::add));
//统计
Map countByState=persons.stream()
.collect(分组依据(Person::getState,counting());
//合并
replaceAll((state,sum)->sum.divide(BigDecimal.valueOf(countByState.get(state)));
返回萨姆比州;
}

您需要保留BigDecimal精度,还是结果可以是double?很遗憾,是的。有了double,这很容易,因为我可以只使用averagingDouble函数。如果你不追求精度,你可以只做
Stream.of(p1,p2,p3,p4)。collect(groupingBy(Person::getState,averagingDouble(p->p.getSalary().doubleValue())
仅仅为了使用方法引用就值得添加映射收集器吗
Map<String, BigDecimal> salariesByState = Stream.of(p1, p2, p3, p4).collect(
    Collectors.groupingBy(Person::getState,
        Collectors.mapping(Person::getSalary, averagingBigDecimal())));
static Map<String, BigDecimal> averageByState(List<Person> persons) {
    // collect sums
    Map<String, BigDecimal> sumByState = persons.stream()
            .collect(groupingBy(
                    Person::getState,
                    HashMap::new,
                    mapping(Person::getSalary, reducing(BigDecimal.ZERO, BigDecimal::add))));

    // collect counts
    Map<String, Long> countByState = persons.stream()
            .collect(groupingBy(Person::getState, counting()));

    // merge
    sumByState.replaceAll((state, sum) -> sum.divide(BigDecimal.valueOf(countByState.get(state))));
    return sumByState;
}