前言:
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 + "]";
}
}
}
测试结果,求平均支持四舍五入,直接截取等等小数保留位算法: