利用stream对list集合中的bigdecimal进行分组求和,均值,最大值,最小值

Posted 醉酒的小男人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用stream对list集合中的bigdecimal进行分组求和,均值,最大值,最小值相关的知识,希望对你有一定的参考价值。

Java8原生只提供了summingInt、summingLong、summingDouble三种基础类型的方法,想要对BigDecimal类型的数据操作需要自己新建工具类如下:

案例代码

简单的方式取值:

BigDecimal totalPrice = list.stream().map(ProductA::getPrice).reduce(BigDecimal.ZERO, BigDecimal::add);

创建一个函数式接口:

package utils;
import java.math.BigDecimal;

@FunctionalInterface
public interface ToBigDecimalFunction<T> {
    BigDecimal applyAsBigDecimal(T value);
}

  创建工具类

package utils;
import java.math.BigDecimal;
import java.util.Collections;
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;

public class CollectorsUtil {
    static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();

    private CollectorsUtil() {
    }

    @SuppressWarnings("unchecked")
    private static <I, R> Function<I, R> castingIdentity() {
        return i -> (R) i;
    }

    static class CollectorImpl<T, A, R> implements Collector<T, A, R> {
        private final Supplier<A> supplier;
        private final BiConsumer<A, T> accumulator;
        private final BinaryOperator<A> combiner;
        private final Function<A, R> finisher;
        private final Set<Characteristics> characteristics;

        CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner,
                      Function<A, R> finisher, Set<Characteristics> characteristics) {
            this.supplier = supplier;
            this.accumulator = accumulator;
            this.combiner = combiner;
            this.finisher = finisher;
            this.characteristics = characteristics;
        }

        CollectorImpl(Supplier<A> supplier, BiConsumer<A, T> accumulator, BinaryOperator<A> combiner,
                      Set<Characteristics> characteristics) {
            this(supplier, accumulator, combiner, castingIdentity(), characteristics);
        }

        @Override
        public BiConsumer<A, T> accumulator() {
            return accumulator;
        }

        @Override
        public Supplier<A> supplier() {
            return supplier;
        }

        @Override
        public BinaryOperator<A> combiner() {
            return combiner;
        }

        @Override
        public Function<A, R> finisher() {
            return finisher;
        }

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

    //求和方法
    public static <T> Collector<T, ?, BigDecimal> summingBigDecimal(ToBigDecimalFunction<? super T> mapper) {
        return new CollectorImpl<>(
                () -> new BigDecimal[]{BigDecimal.ZERO},
                (a, t) -> { a[0] = a[0].add(mapper.applyAsBigDecimal(t)); },
                (a, b) -> { a[0] = a[0].add(b[0]) ; return a; },
                a -> a[0], CH_NOID);
    }

    //求最大值
    public static <T> Collector<T, ?, BigDecimal> maxBy(ToBigDecimalFunction<? super T> mapper) {
        return new CollectorImpl<>(
                () -> new BigDecimal[]{new BigDecimal(Long.MIN_VALUE)},
                (a, t) -> { a[0] = a[0].max(mapper.applyAsBigDecimal(t)); },
                (a, b) -> { a[0] = a[0].max(b[0]) ; return a; },
                a -> a[0], CH_NOID);
    }

    //求最小值
    public static <T> Collector<T, ?, BigDecimal> minBy(ToBigDecimalFunction<? super T> mapper) {
        return new CollectorImpl<>(
                () -> new BigDecimal[]{new BigDecimal(Long.MAX_VALUE)},
                (a, t) -> { a[0] = a[0].min(mapper.applyAsBigDecimal(t)); },
                (a, b) -> { a[0] = a[0].min(b[0]) ; return a; },
                a -> a[0], CH_NOID);
    }

    /**
     * 求平均值
     * @param mapper
     * @param newScale 保留newScale位小数
     * @param roundingMode 舍去规则(0 <= roundingMode <= 7)
     * @param <T>
     * @return
     */
    public static <T> Collector<T, ?, BigDecimal> averagingBigDecimal(ToBigDecimalFunction<? super T> mapper, int newScale, int roundingMode) {
        return new CollectorImpl<>(
                () -> new BigDecimal[]{BigDecimal.ZERO,BigDecimal.ZERO},
                (a, t) -> { a[0] = a[0].add(mapper.applyAsBigDecimal(t)); a[1] = a[1].add(BigDecimal.ONE); },
                (a, b) -> { a[0] = a[0].add(b[0]) ; return a; },
                a -> a[0].divide(a[1],BigDecimal.ROUND_HALF_UP).setScale(newScale, roundingMode), CH_NOID);
    }
}

创建Student类:

package utils;
import java.math.BigDecimal;

public class Student {
    private Integer id;
    private String sex;
    private Integer age;
    private BigDecimal score;

    public Student(Integer id,String sex, Integer age, BigDecimal score) {
        this.id = id;
        this.sex = sex;
        this.age = age;
        this.score = score;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public BigDecimal getScore() {
        return score;
    }

    public void setScore(BigDecimal score) {
        this.score = score;
    }
}

测试类

package utils;

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

public class StreamTest {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        list.add(new Student(1,"男",18,new BigDecimal(100)));
        list.add(new Student(2,"男",19,new BigDecimal(90)));
        list.add(new Student(3,"女",20,new BigDecimal(80)));
        list.add(new Student(4,"女",20,new BigDecimal(70)));
        list.add(new Student(5,"女",20,null));

        //单条件筛选
        //按照性别分组求分数总和
        Map<String, BigDecimal> scoreCount = list.stream().filter(t -> t.getScore() != null).collect(Collectors.groupingBy(Student::getSex, CollectorsUtil.summingBigDecimal(Student::getScore)));
        System.out.println("----按照性别分组求分数总和----");
        scoreCount.forEach((k,v) -> System.out.println("key: " + k + " , " + "value: " + v));

       //按照性别求分数平均值
        Map<String, BigDecimal> scoreAvg = list.stream().filter(t -> t.getScore() != null).collect(Collectors.groupingBy(Student::getSex, CollectorsUtil.averagingBigDecimal(Student::getScore,2,0)));
        System.out.println("----按照性别求分数平均值----");
        scoreAvg.forEach((k,v) -> System.out.println("key: " + k + " , " + "value: " + v));



        //按照性别年龄分组求分数总和
        Map<String, BigDecimal> ageScoreCount = list.stream().filter(t -> t.getScore() != null).collect(Collectors.groupingBy(p -> fetchGroupKey(p), CollectorsUtil.summingBigDecimal(Student::getScore)));
        System.out.println("----按照性别年龄分组求分数总和----");
        ageScoreCount.forEach((k,v) -> System.out.println("key: " + k + " , " + "value: " + v));

        //按照性别年龄分组求分数平均值
        Map<String, BigDecimal> ageScoreAvg = list.stream().filter(t -> t.getScore() != null).collect(Collectors.groupingBy(p -> fetchGroupKey(p), CollectorsUtil.averagingBigDecimal(Student::getScore,2,1)));
        System.out.println("----按照性别年龄分组求分数平均值----");
        ageScoreAvg.forEach((k,v) -> System.out.println("key: " + k + " , " + "value: " + v));
    }

    //多条件筛选
    //多条件筛选分组属性
    private static String fetchGroupKey(Student stu) {
        return stu.getAge() + "#" + stu.getSex();
    }
}

执行结果

 BigDecimal.setScale用法

BigDecimal.setScale(int newScale, int roundingMode)

newScale: 保留newScale位小数
roundingMode: 舍去规则(0 <= roundingMode <= 7)

一、BigDecimal.ROUND_UP=0

BigDecimal num = new BigDecimal("3.161590");
// 进位处理,不进行四舍五入(精度后的位数是0不进位)
System.out.println(num.setScale(1, BigDecimal.ROUND_UP)); // 3.2
System.out.println(num.setScale(2, BigDecimal.ROUND_UP)); // 3.17
System.out.println(num.setScale(5, BigDecimal.ROUND_UP)); // 3.16159

 二、BigDecimal.ROUND_DOWN=1

BigDecimal num = new BigDecimal("3.16159");
// 直接舍去多余的位数,不进行四舍五入
System.out.println(num.setScale(1, BigDecimal.ROUND_DOWN)); // 3.1
System.out.println(num.setScale(2, BigDecimal.ROUND_DOWN)); // 3.16

 三、BigDecimal.ROUND_CEILING=2

BigDecimal num = new BigDecimal("3.161590");
// 天花板(向上),正数进位向上,同ROUND_UP
System.out.println(num.setScale(1, BigDecimal.ROUND_CEILING)); // 3.2
System.out.println(num.setScale(2, BigDecimal.ROUND_CEILING)); // 3.17
System.out.println(num.setScale(5, BigDecimal.ROUND_CEILING)); // 3.16159
BigDecimal num = new BigDecimal("-3.161590");
// 天花板(向上),负数舍位向上,同ROUND_DOWN
System.out.println(num.setScale(1, BigDecimal.ROUND_CEILING)); // -3.1
System.out.println(num.setScale(2, BigDecimal.ROUND_CEILING)); // -3.16
System.out.println(num.setScale(5, BigDecimal.ROUND_CEILING)); // -3.16159

 四、BigDecimal.ROUND_FLOOR=3

BigDecimal num = new BigDecimal("3.161590");
// 地板(向下),正数舍位向下
System.out.println(num.setScale(1, BigDecimal.ROUND_FLOOR)); // 3.1
System.out.println(num.setScale(2, BigDecimal.ROUND_FLOOR)); // 3.16
BigDecimal num = new BigDecimal("-3.161590");
// 地板(向下),负数进位向下
System.out.println(num.setScale(1, BigDecimal.ROUND_FLOOR)); // -3.2
System.out.println(num.setScale(2, BigDecimal.ROUND_FLOOR)); // -3.17

五、 BigDecimal.ROUND_HALF_UP=4

BigDecimal num = new BigDecimal("3.161590");
// 正常四舍五入
System.out.println(num.setScale(1, BigDecimal.ROUND_HALF_UP)); // 3.2
System.out.println(num.setScale(2, BigDecimal.ROUND_HALF_UP)); // 3.16

六、BigDecimal.ROUND_HALF_DOWN=5

BigDecimal num = new BigDecimal("3.161500");
// 四舍五入(若舍弃部分>.5,就进位)
System.out.println(num.setScale(1, BigDecimal.ROUND_HALF_DOWN)); // 3.2
System.out.println(num.setScale(2, BigDecimal.ROUND_HALF_DOWN)); // 3.16
System.out.println(num.setScale(3, BigDecimal.ROUND_HALF_DOWN)); // 3.161
num = new BigDecimal("3.161590");
// .590 > .5
System.out.println(num.setScale(3, BigDecimal.ROUND_HALF_DOWN)); // 3.162

七、BigDecimal.ROUND_HALF_EVEN=6

BigDecimal num = new BigDecimal("3.46159");
// 如果舍弃部分左边的数字为偶数,则作 ROUND_HALF_DOWN
System.out.println(num.setScale(1, BigDecimal.ROUND_HALF_EVEN)); // 3.5
System.out.println(num.setScale(2, BigDecimal.ROUND_HALF_EVEN)); // 3.46
BigDecimal num = new BigDecimal("3.37459");
// 如果舍弃部分左边的数字为奇数,则作 ROUND_HALF_UP
System.out.println(num.setScale(1, BigDecimal.ROUND_HALF_EVEN)); // 3.4
System.out.println(num.setScale(2, BigDecimal.ROUND_HALF_EVEN)); // 3.37

八、BigDecimal.ROUND_UNNECESSARY=7 

BigDecimal num = new BigDecimal("3.37459");
// 断言请求的操作具有精确的结果
System.out.println(num.setScale(5, BigDecimal.ROUND_UNNECESSARY)); // 3.37459
System.out.println(num.setScale(2, BigDecimal.ROUND_UNNECESSARY)); // 抛出ArithmeticException

如果有5位小数,那么指定舍5位的话,会正确输出结果。但是指定的位数不是num的位数,即不是5位,那么会抛出ArithmeticException异常,这样就可以检证num的小数位数。

以上是关于利用stream对list集合中的bigdecimal进行分组求和,均值,最大值,最小值的主要内容,如果未能解决你的问题,请参考以下文章

有两个ArrayList集合,存储队伍中的多个成员姓名,使用Stream方式,对以下步骤进行操作

利用stream对map集合进行过滤

使用流和求和方法的BigDecimal List的总和

Lambda处理List集合

Java8 Stream流使用及其基本原理

Java有关List的stream基本操作