Java之BigDecimal的高级使用
- 2019 年 10 月 8 日
- 筆記
引入
使用Java开发的朋友,对于数据相关的计算想必都有过头疼的经历。float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。今天就分享一个关于小数精确计算的类(BigDecimal)高级用法。
1、加减乘除的简单封装;
2、引入Lambda表达式的聚合计算;
3、关于复杂对象的数组根据某一字段的值聚合计算;
0
2
撸代码
封装,是身为一个好程序猿的必备技能。往往通过一次封装之后的工具类,在使用起来会让人赏心悦目,心里那叫一个舒坦。
前提:Java8+(为了使用Lambda)
注意点:BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以在做加减乘除运算时千万要保存操作后的值。
1、首先,是加减乘除。
import lombok.AccessLevel; import lombok.NoArgsConstructor; import java.math.BigDecimal; /** * 以double传参为例 * 如果有各种嗜好,以在此基础上任意添加 */ @NoArgsConstructor(access = AccessLevel.PRIVATE) public class BigDecimalUtil { /** * 加 */ public static BigDecimal add(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.add(b2); } /** * 减 */ public static BigDecimal sub(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.subtract(b2); } /** * 乘 */ public static BigDecimal mul(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.multiply(b2); } /** * 除,四舍五入保留2位小数 */ public static BigDecimal div(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.divide(b2, 2, BigDecimal.ROUND_HALF_UP); } /** * 除,四舍五入保留n位小数 */ public static BigDecimal div(double v1, double v2, int n) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.divide(b2, n, BigDecimal.ROUND_HALF_UP); } }
0
3
聚合计算
场景一:现在有一个User对象,需要计算所有User的总数money。
场景二:需要根据性别分组,计算不同性别的money总数。
User对象
import lombok.Data; import java.math.BigDecimal; /** * @Auther: bboyHan * @Date: 2019/1/18 18:10 * @Description: */ @Data public class User { private String name; private int gender; //1 - 男;2 - 女 0 - XXX private BigDecimal money; }
2、引入Lambda表达式的聚合计算。(场景一)
//传统的求和,可能会是遍历list BigDecimal rs = BigDecimal.ZERO; List<BigDecimal> list = new ArrayList<>(); list.add(new BigDecimal("1")); list.add(new BigDecimal("2")); list.add(new BigDecimal("3")); list.add(new BigDecimal("4")); list.add(new BigDecimal("5")); for (BigDecimal big : list) { rs = rs.add(big); }
对于User这样的对象也可以照葫芦画瓢,通过foreach的方式add(user.getMoney)得到。
import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; /** * @Auther: bboyHan * @Date: 2019/1/18 18:27 * @Description: */ public class BigdecimalTest { public static void main(String[] args) { List<User> list = new ArrayList<>(); list.add(new User("张三",1,new BigDecimal(10))); list.add(new User("老王",2,new BigDecimal(20))); list.add(new User("大佬",0,new BigDecimal(30))); BigDecimal rs = list.stream().map(User::getMoney).reduce(BigDecimal.ZERO, BigDecimal::add); } }
3、复杂对象的分组求和。(场景二)
(1)新建CollectionsUtil
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; } /** * Simple implementation class for {@code Collector}. * * @param <T> * the type of elements to be collected * @param <R> * the type of the result */ 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[1], (a, t) -> { if (a[0] == null) { a[0] = BigDecimal.ZERO; } a[0] = a[0].add(mapper.applyAsBigDecimal(t)); }, (a, b) -> { a[0] = a[0].add(b[0]); return a; }, a -> a[0], CH_NOID); } }
(2)新建ToBigDecimalFunction
import java.math.BigDecimal; @FunctionalInterface public interface ToBigDecimalFunction<T> { BigDecimal applyAsBigDecimal(T value); }
(3)使用
import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * @Auther: bboyHan * @Date: 2019/1/18 18:29 * @Description: */ public class BigdecimalTest { public static void main(String[] args) { List<User> list = new ArrayList<>(); list.add(new User("张三", 1, new BigDecimal(10))); list.add(new User("老王", 2, new BigDecimal(20))); list.add(new User("大佬", 0, new BigDecimal(30))); Map<Integer, BigDecimal> rs = list.stream() .collect( Collectors.groupingBy(User::getGender, CollectorsUtil.summingBigDecimal(User::getMoney)) ); } }
0
4
小结
这么简单,就不总结了吧。拿出你的CV大法直接使用吧。
谢谢~
内容部分参考网上某大佬,记得好像是在云栖,不太记得了,在此鸣谢。
