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大法直接使用吧。
謝謝~
內容部分參考網上某大佬,記得好像是在雲棲,不太記得了,在此鳴謝。
