Java8关于BigDecimal的求和,求平均,最小,最大,支持分组

前言:

java8的label表达式功能非常强大,精简代码,提高代码效率非常有效,尤其是对集合上的支持可谓空前强大,比如分组求和,最大,筛选,排序,不过这些支持一般是针对普通数据类型的,但是项目当中很多时候考虑到小数精度问题会抛弃Double数据类型,而采用BigDecimal,虽然java8也支持BigDecimal的求和,平均等等,不过写法非常麻烦,花了半天时间研究其源码,并且对其进行改写,基本上符合大部分业务场景。废话不多说,直接上代码,转载请标明连接出处。

定义接口

import java.math.BigDecimal;

@FunctionalInterface
public interface ToBigDecimalFunction <T> {

  BigDecimal applyAsBigDecimal(T value);

}

编写自定义的Collectors

import java.math.BigDecimal;
import java.math.MathContext;
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;

/**
 *
 * @author chen.kai
 * 2019年12月14日
 * <p>
 *    因java8没有关于Bigdecimal求和的一类方法,这里根据java8包含的int,double求和的方法,进行修改,变为自己合适的方法
 * </p>
 */
public final class CustomCollectors {

  private static final Set<Collector.Characteristics> CH_NOID = Collections.emptySet();

  private CustomCollectors() {}


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

  /**
   * Simple implementation class for {@code Collector}.
   *
   * @param <T> the type of elements to be collected
   * @param <R> the type of the result
   */
  @SuppressWarnings("hiding")
  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[]{new BigDecimal(0)},
            (a, t) -> { a[0] = a[0].add(mapper.applyAsBigDecimal(t), MathContext.DECIMAL32); },
            (a, b) -> { a[0] = a[0].add(b[0], MathContext.DECIMAL32) ; return a; },
            a -> a[0], CH_NOID);
  }


  //求最大,这里的最小MIN值,作为初始条件判断值,如果某些数据范围超过百亿以后,可以根据需求换成Long.MIN_VALUE或者Double.MIN_VALUE
  public static <T> Collector<T, ?, BigDecimal> maxBy(ToBigDecimalFunction<? super T> mapper) {
    return new CollectorImpl<>(
            () -> new BigDecimal[]{new BigDecimal(Integer.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);
  }

  //求最小,这里的最大MAX值,作为初始条件判断值,如果某些数据范围超过百亿以后,可以根据需求换成Long.MAX_VALUE或者Double.MAX_VALUE
  public static <T> Collector<T, ?, BigDecimal> minBy(ToBigDecimalFunction<? super T> mapper) {
    return new CollectorImpl<>(
            () -> new BigDecimal[]{new BigDecimal(Integer.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 newScale 保留小数位数
   * @param roundingMode 小数处理方式
   *     #ROUND_UP 进1
   *     #ROUND_DOWN 退1
   *     #ROUND_CEILING  进1截取:正数则ROUND_UP,负数则ROUND_DOWN
   *     #ROUND_FLOOR  退1截取:正数则ROUND_DOWN,负数则ROUND_UP
   *     #ROUND_HALF_UP >=0.5进1
   *     #ROUND_HALF_DOWN >0.5进1
   *     #ROUND_HALF_EVEN
   *     #ROUND_UNNECESSARY
   */
  //求平均,并且保留小数
  public static <T> Collector<T, ?, BigDecimal> averagingBigDecimal(ToBigDecimalFunction<? super T> mapper, int newScale, int roundingMode) {
    return new CollectorImpl<>(
            () -> new BigDecimal[]{new BigDecimal(0),new BigDecimal(0)},
            (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],MathContext.DECIMAL32).setScale(newScale,roundingMode), CH_NOID);
  }


}

测试:

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

import util.CustomCollectors;



/**
 * @author chen.kai
 * @date 2019年12月14日
 * @description 
 */
public class Test2 {
    
    public static void main(String[] args) {
        //测试数据主备
        List<Student> list = new ArrayList<Student>();
        list.add(new Student(1, "张三", new BigDecimal(-1)));
        list.add(new Student(1, "张三", new BigDecimal(180.5)));
        list.add(new Student(1, "张三", new BigDecimal(170.6)));
        list.add(new Student(2, "李四", new BigDecimal(160)));
        
        Map<Integer, BigDecimal> map = list.stream().collect(
                Collectors.groupingBy(Student::getId,CustomCollectors.summingBigDecimal(Student::getHeight)));
        System.out.println("求和的数据" + map.toString());
        System.out.println("=======================================");
        Map<Integer, BigDecimal> map1 = list.stream().collect(
                Collectors.groupingBy(Student::getId,CustomCollectors.averagingBigDecimal(Student::getHeight, 2, 2)));
        System.out.println("求平均值" + map1.toString());
        System.out.println("----------------------------------------");
        Map<Integer, BigDecimal> map2 = list.stream().collect(
                Collectors.groupingBy(Student::getId,CustomCollectors.maxBy(Student::getHeight)));
//        System.out.println(new BigDecimal(170.11).add(new BigDecimal(1), MathContext.DECIMAL32));
        System.out.println("求最大的值为:" +map2.toString());
        System.out.println("----------------------------------------");
        Map<Integer, BigDecimal> map3 = list.stream().collect(
                Collectors.groupingBy(Student::getId,CustomCollectors.minBy(Student::getHeight)));
        System.out.println("分组后的最小值为:" + map3.toString());
        
    }

    
    static class Student {
        private Integer id;
        
        private String name;
        
        private BigDecimal height;

        public Student() {}
        
        /**
         * @param id
         * @param name
         * @param height
         */
        public Student(Integer id, String name, BigDecimal height) {
            super();
            this.id = id;
            this.name = name;
            this.height = height;
        }

        /**
         * @return the id
         */
        public Integer getId() {
            return id;
        }

        /**
         * @param id the id to set
         */
        public void setId(Integer id) {
            this.id = id;
        }

        /**
         * @return the name
         */
        public String getName() {
            return name;
        }

        /**
         * @param name the name to set
         */
        public void setName(String name) {
            this.name = name;
        }

        /**
         * @return the height
         */
        public BigDecimal getHeight() {
            return height;
        }

        /**
         * @param height the height to set
         */
        public void setHeight(BigDecimal height) {
            this.height = height;
        }

        /* (non-Javadoc)
         * @see java.lang.Object#toString()
         */
        @Override
        public String toString() {
            return "Student [id=" + id + ", name=" + name + ", height=" + height + "]";
        }
        
        
    }
}

测试结果,求平均支持四舍五入,直接截取等等小数保留位算法:
在这里插入图片描述

发布了4 篇原创文章 · 获赞 0 · 访问量 157

猜你喜欢

转载自blog.csdn.net/wohennis1/article/details/103834296